@firela/api-types 0.0.0-canary.4e161540 → 0.0.0-canary.4f9bd799

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -181,7 +189,8 @@ export const $AccountResponseDto = {
181
189
  },
182
190
  displayName: {
183
191
  type: 'string',
184
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
185
194
  example: 'Checking'
186
195
  },
187
196
  icon: {
@@ -197,7 +206,7 @@ export const $AccountResponseDto = {
197
206
  }
198
207
  },
199
208
  platformId: {
200
- type: 'object',
209
+ type: 'string',
201
210
  description: 'Platform ID (null if unbound)',
202
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
203
212
  },
@@ -277,6 +286,14 @@ export const $UpdateAccountDto = {
277
286
  description: 'Icon identifier',
278
287
  example: 'bank-custom'
279
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
280
297
  openDirectiveMeta: {
281
298
  type: 'object',
282
299
  description:
@@ -377,16 +394,43 @@ export const $AccountStandardResponseDto = {
377
394
  },
378
395
  name: {
379
396
  type: 'string',
380
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
381
399
  example: 'Housing Fund'
382
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
383
425
  description: {
384
426
  type: 'string',
385
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
386
429
  example: 'ICBC checking account for daily transactions'
387
430
  },
388
431
  tags: {
389
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
390
434
  example: ['bank', 'checking', 'primary'],
391
435
  type: 'array',
392
436
  items: {
@@ -498,7 +542,10 @@ export const $RegionConfigDto = {
498
542
  },
499
543
  locale: {
500
544
  type: 'string',
501
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
502
549
  }
503
550
  },
504
551
  required: ['currency', 'dateFormat', 'locale']
@@ -511,6 +558,12 @@ export const $RegionInfoDto = {
511
558
  type: 'string',
512
559
  example: 'de'
513
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
514
567
  displayName: {
515
568
  type: 'string',
516
569
  example: 'Germany'
@@ -529,7 +582,7 @@ export const $RegionInfoDto = {
529
582
  $ref: '#/components/schemas/RegionConfigDto'
530
583
  }
531
584
  },
532
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
533
586
  } as const;
534
587
 
535
588
  export const $RegionsMetadataResponseDto = {
@@ -776,7 +829,7 @@ export const $PostingResponseDto = {
776
829
  units: {
777
830
  type: 'string',
778
831
  description:
779
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
780
833
  example: '100.50'
781
834
  },
782
835
  currency: {
@@ -1142,7 +1195,7 @@ export const $PostingDetailDto = {
1142
1195
  units: {
1143
1196
  type: 'string',
1144
1197
  description:
1145
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1146
1199
  example: '100.50'
1147
1200
  },
1148
1201
  currency: {
@@ -1326,6 +1379,147 @@ export const $TransactionDetailDto = {
1326
1379
  ]
1327
1380
  } as const;
1328
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1329
1523
  export const $BalanceByCurrencyDto = {
1330
1524
  type: 'object',
1331
1525
  properties: {
@@ -1371,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1371
1565
  totalAmount: {
1372
1566
  type: 'string',
1373
1567
  description:
1374
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1568
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1375
1569
  example: '-6000.00'
1376
1570
  },
1377
1571
  currency: {
@@ -1397,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1397
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1398
1592
  } as const;
1399
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1400
1614
  export const $TransactionListResponseDto = {
1401
1615
  type: 'object',
1402
1616
  properties: {
@@ -1404,7 +1618,7 @@ export const $TransactionListResponseDto = {
1404
1618
  description: 'List of transactions',
1405
1619
  type: 'array',
1406
1620
  items: {
1407
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1408
1622
  }
1409
1623
  },
1410
1624
  total: {
@@ -1430,6 +1644,15 @@ export const $TransactionListResponseDto = {
1430
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1431
1645
  }
1432
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1433
1656
  }
1434
1657
  },
1435
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -2340,10 +2563,10 @@ export const $UpdatePayeeDto = {
2340
2563
  meta: {
2341
2564
  type: 'object',
2342
2565
  description:
2343
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2566
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2344
2567
  example: {
2345
2568
  location: 'Zhongguancun',
2346
- note: 'Updated note',
2569
+ note: 'Near subway station',
2347
2570
  favorite: true
2348
2571
  }
2349
2572
  },
@@ -2904,568 +3127,660 @@ export const $UpdateCommodityDto = {
2904
3127
  }
2905
3128
  } as const;
2906
3129
 
2907
- export const $CreateBeanPriceDto = {
3130
+ export const $CurrencyBalanceDto = {
2908
3131
  type: 'object',
2909
3132
  properties: {
2910
3133
  currency: {
2911
3134
  type: 'string',
2912
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2913
- example: 'USD'
2914
- },
2915
- quoteCurrency: {
2916
- type: 'string',
2917
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3135
+ description: 'ISO 4217 currency code',
2918
3136
  example: 'CNY'
2919
3137
  },
2920
- amount: {
2921
- type: 'number',
2922
- description:
2923
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2924
- example: 175.5,
2925
- minimum: 0
2926
- },
2927
- date: {
3138
+ balance: {
2928
3139
  type: 'string',
2929
- description: 'Price date (ISO 8601 format)',
2930
- example: '2024-11-05'
2931
- },
2932
- metadata: {
2933
- type: 'object',
2934
- description:
2935
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2936
- example: {
2937
- source: 'MANUAL',
2938
- note: 'Bank valuation report',
2939
- confidence: 0.95
2940
- }
3140
+ description: 'Balance amount',
3141
+ example: '500000.00'
2941
3142
  }
2942
3143
  },
2943
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3144
+ required: ['currency', 'balance']
2944
3145
  } as const;
2945
3146
 
2946
- export const $PriceResponseDto = {
3147
+ export const $TimeSeriesPointDto = {
2947
3148
  type: 'object',
2948
3149
  properties: {
2949
- id: {
3150
+ date: {
2950
3151
  type: 'string',
2951
- description: 'Unique identifier',
2952
- example: 'uuid-123-456'
3152
+ description: 'Date in YYYY-MM-DD format',
3153
+ example: '2024-06-15'
2953
3154
  },
2954
- userId: {
3155
+ value: {
2955
3156
  type: 'string',
2956
- description: 'User ID (owner of the price)',
2957
- example: 'user-123'
3157
+ description: 'Value at this date (in base currency)',
3158
+ example: '500000.00'
2958
3159
  },
2959
- currency: {
3160
+ change: {
2960
3161
  type: 'string',
2961
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2962
- example: 'BTC'
3162
+ description: 'Change from previous point',
3163
+ example: '5000.00'
2963
3164
  },
2964
- quoteCurrency: {
3165
+ assets: {
2965
3166
  type: 'string',
2966
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2967
- example: 'USD'
3167
+ description: 'Total assets at this date (in base currency)',
3168
+ example: '494338.00'
2968
3169
  },
2969
- amount: {
2970
- type: 'number',
2971
- description:
2972
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2973
- example: 50000
2974
- },
2975
- date: {
2976
- type: 'string',
2977
- description:
2978
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2979
- example: '2024-01-01',
2980
- format: 'date'
2981
- },
2982
- meta: {
2983
- type: 'object',
2984
- description:
2985
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2986
- example: {
2987
- source: 'MANUAL',
2988
- note: 'User-defined price',
2989
- confidence: 1
2990
- }
2991
- },
2992
- createdAt: {
2993
- format: 'date-time',
3170
+ liabilities: {
2994
3171
  type: 'string',
2995
- description: 'Creation timestamp',
2996
- example: '2024-11-03T10:00:00Z'
3172
+ description: 'Total liabilities at this date (in base currency)',
3173
+ example: '310098.00'
2997
3174
  },
2998
- updatedAt: {
2999
- format: 'date-time',
3000
- type: 'string',
3001
- description: 'Last update timestamp',
3002
- example: '2024-11-03T10:00:00Z'
3003
- }
3004
- },
3005
- required: [
3006
- 'id',
3007
- 'userId',
3008
- 'currency',
3009
- 'quoteCurrency',
3010
- 'amount',
3011
- 'date',
3012
- 'meta',
3013
- 'createdAt',
3014
- 'updatedAt'
3015
- ]
3016
- } as const;
3017
-
3018
- export const $PriceListResponseDto = {
3019
- type: 'object',
3020
- properties: {
3021
- items: {
3022
- description: 'List of prices',
3175
+ byCurrency: {
3176
+ description: 'Multi-currency breakdown for this point',
3023
3177
  type: 'array',
3024
3178
  items: {
3025
- $ref: '#/components/schemas/PriceResponseDto'
3179
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3026
3180
  }
3027
- },
3028
- total: {
3029
- type: 'number',
3030
- description: 'Total number of prices',
3031
- example: 42
3032
3181
  }
3033
3182
  },
3034
- required: ['items', 'total']
3183
+ required: ['date', 'value']
3035
3184
  } as const;
3036
3185
 
3037
- export const $UpdateBeanPriceDto = {
3186
+ export const $TrendSummaryDto = {
3038
3187
  type: 'object',
3039
3188
  properties: {
3040
- currency: {
3189
+ startValue: {
3041
3190
  type: 'string',
3042
- description: 'Currency being priced'
3191
+ description: 'Value at start of period',
3192
+ example: '450000.00'
3043
3193
  },
3044
- quoteCurrency: {
3194
+ endValue: {
3045
3195
  type: 'string',
3046
- description: 'Quote currency (pricing currency)'
3047
- },
3048
- amount: {
3049
- type: 'number',
3050
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3051
- minimum: 0
3196
+ description: 'Value at end of period',
3197
+ example: '500000.00'
3052
3198
  },
3053
- date: {
3199
+ totalChange: {
3054
3200
  type: 'string',
3055
- description: 'Price date (ISO 8601 format)'
3201
+ description: 'Total change over period',
3202
+ example: '50000.00'
3056
3203
  },
3057
- metadata: {
3058
- type: 'object',
3059
- description: 'Metadata'
3204
+ totalChangePercentage: {
3205
+ type: 'string',
3206
+ description: 'Total change percentage',
3207
+ example: '+11.11%'
3060
3208
  }
3061
- }
3209
+ },
3210
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3062
3211
  } as const;
3063
3212
 
3064
- export const $CreateRecurringRuleDto = {
3213
+ export const $MultiCurrencyPointDto = {
3065
3214
  type: 'object',
3066
3215
  properties: {
3067
- name: {
3216
+ date: {
3068
3217
  type: 'string',
3069
- description: 'Rule name (unique per user)',
3070
- maxLength: 100
3218
+ description: 'Date in YYYY-MM-DD format',
3219
+ example: '2024-06-15'
3071
3220
  },
3072
- icon: {
3073
- type: 'string',
3074
- description: 'Icon emoji',
3075
- maxLength: 10
3221
+ byCurrency: {
3222
+ description: 'Balances by currency',
3223
+ type: 'array',
3224
+ items: {
3225
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3226
+ }
3227
+ }
3228
+ },
3229
+ required: ['date', 'byCurrency']
3230
+ } as const;
3231
+
3232
+ export const $PortfolioTrendsResponseDto = {
3233
+ type: 'object',
3234
+ properties: {
3235
+ series: {
3236
+ description: 'Time series data points',
3237
+ type: 'array',
3238
+ items: {
3239
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3240
+ }
3076
3241
  },
3077
- frequency: {
3078
- type: 'string',
3079
- description: 'Recurring frequency',
3080
- enum: [
3081
- 'WEEKLY',
3082
- 'BIWEEKLY',
3083
- 'MONTHLY',
3084
- 'BIMONTHLY',
3085
- 'QUARTERLY',
3086
- 'YEARLY',
3087
- 'CUSTOM'
3242
+ summary: {
3243
+ description: 'Period summary',
3244
+ allOf: [
3245
+ {
3246
+ $ref: '#/components/schemas/TrendSummaryDto'
3247
+ }
3088
3248
  ]
3089
3249
  },
3090
- expectedAmount: {
3091
- type: 'number',
3092
- description: 'Expected amount (positive number)',
3093
- minimum: 0
3094
- },
3095
- expectedDay: {
3096
- type: 'number',
3097
- description: 'Expected day of month (1-31)',
3098
- minimum: 1,
3099
- maximum: 31
3100
- },
3101
- customIntervalDays: {
3102
- type: 'number',
3103
- description: 'Custom interval in days (required for CUSTOM frequency)',
3104
- minimum: 1
3105
- },
3106
- currency: {
3250
+ period: {
3107
3251
  type: 'string',
3108
- description: 'Currency code',
3109
- default: 'CNY',
3110
- maxLength: 10
3252
+ description: 'Period requested',
3253
+ example: '6m'
3111
3254
  },
3112
- matchPayeePattern: {
3255
+ granularity: {
3113
3256
  type: 'string',
3114
- description: 'Payee matching pattern (supports wildcards)',
3115
- maxLength: 200
3116
- },
3117
- matchAmountTolerance: {
3118
- type: 'number',
3119
- description: 'Amount tolerance percentage (0-1)',
3120
- default: 0.075,
3121
- minimum: 0,
3122
- maximum: 1
3257
+ description: 'Data granularity',
3258
+ example: 'month'
3123
3259
  },
3124
- defaultExpenseAccount: {
3260
+ currency: {
3125
3261
  type: 'string',
3126
- description: 'Default expense account for auto-create',
3127
- maxLength: 200
3262
+ description: 'Base currency for converted values',
3263
+ example: 'CNY'
3128
3264
  },
3129
- defaultPaymentAccount: {
3130
- type: 'string',
3131
- description: 'Default payment account for auto-create',
3132
- maxLength: 200
3265
+ byCurrency: {
3266
+ description:
3267
+ 'Multi-currency time series (each point has currency breakdown)',
3268
+ type: 'array',
3269
+ items: {
3270
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3271
+ }
3133
3272
  },
3134
- defaultPayee: {
3273
+ warnings: {
3274
+ description: 'Exchange rate warnings',
3275
+ type: 'array',
3276
+ items: {
3277
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3278
+ }
3279
+ }
3280
+ },
3281
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3282
+ } as const;
3283
+
3284
+ export const $CashFlowPointDto = {
3285
+ type: 'object',
3286
+ properties: {
3287
+ month: {
3135
3288
  type: 'string',
3136
- description: 'Default payee for auto-create',
3137
- maxLength: 200
3289
+ description: 'Month key (YYYY-MM)',
3290
+ example: '2024-03'
3138
3291
  },
3139
- autoCreate: {
3140
- type: 'boolean',
3141
- description: 'Auto-create transaction when expected date arrives',
3142
- default: false
3292
+ income: {
3293
+ type: 'string',
3294
+ description: 'Income in base currency (absolute, converted)',
3295
+ example: '10000.00'
3143
3296
  },
3144
- startDate: {
3297
+ expense: {
3145
3298
  type: 'string',
3146
- description: 'Rule start date (ISO format)'
3299
+ description: 'Expense in base currency (absolute, converted)',
3300
+ example: '5000.00'
3147
3301
  },
3148
- endDate: {
3302
+ netSavings: {
3149
3303
  type: 'string',
3150
- description: 'Rule end date (ISO format)'
3304
+ description: 'netSavings = income − expense (savings positive)',
3305
+ example: '5000.00'
3151
3306
  }
3152
3307
  },
3153
- required: [
3154
- 'name',
3155
- 'frequency',
3156
- 'expectedAmount',
3157
- 'currency',
3158
- 'matchAmountTolerance',
3159
- 'autoCreate'
3160
- ]
3308
+ required: ['month', 'income', 'expense', 'netSavings']
3161
3309
  } as const;
3162
3310
 
3163
- export const $RecurringRuleResponseDto = {
3311
+ export const $CashFlowTrendSummaryDto = {
3164
3312
  type: 'object',
3165
3313
  properties: {
3166
- id: {
3314
+ totalIncome: {
3167
3315
  type: 'string',
3168
- description: 'Rule ID'
3316
+ description: 'Total income across the period',
3317
+ example: '60000.00'
3169
3318
  },
3170
- userId: {
3319
+ totalExpense: {
3171
3320
  type: 'string',
3172
- description: 'User ID'
3321
+ description: 'Total expense across the period',
3322
+ example: '30000.00'
3173
3323
  },
3174
- name: {
3324
+ totalNetSavings: {
3175
3325
  type: 'string',
3176
- description: 'Rule name'
3177
- },
3178
- icon: {
3179
- type: 'object',
3180
- description: 'Icon emoji'
3326
+ description: 'income − expense across the period',
3327
+ example: '30000.00'
3181
3328
  },
3182
- frequency: {
3329
+ averageMonthlyNetSavings: {
3183
3330
  type: 'string',
3184
- description: 'Recurring frequency'
3331
+ description:
3332
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3333
+ example: '5000.00'
3334
+ }
3335
+ },
3336
+ required: [
3337
+ 'totalIncome',
3338
+ 'totalExpense',
3339
+ 'totalNetSavings',
3340
+ 'averageMonthlyNetSavings'
3341
+ ]
3342
+ } as const;
3343
+
3344
+ export const $CashFlowTrendsResponseDto = {
3345
+ type: 'object',
3346
+ properties: {
3347
+ series: {
3348
+ description:
3349
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3350
+ type: 'array',
3351
+ items: {
3352
+ $ref: '#/components/schemas/CashFlowPointDto'
3353
+ }
3185
3354
  },
3186
- expectedAmount: {
3187
- type: 'number',
3188
- description: 'Expected amount'
3355
+ summary: {
3356
+ description: 'Period totals',
3357
+ allOf: [
3358
+ {
3359
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3360
+ }
3361
+ ]
3189
3362
  },
3190
- expectedDay: {
3191
- type: 'object',
3192
- description: 'Expected day of month'
3363
+ period: {
3364
+ type: 'string',
3365
+ description: 'Period requested',
3366
+ example: '6m'
3193
3367
  },
3194
- customIntervalDays: {
3195
- type: 'object',
3196
- description: 'Custom interval in days'
3368
+ granularity: {
3369
+ type: 'string',
3370
+ description: 'Data granularity (v1 returns month buckets)',
3371
+ example: 'month'
3197
3372
  },
3198
3373
  currency: {
3199
3374
  type: 'string',
3200
- description: 'Currency code'
3375
+ description: 'Base currency for converted values',
3376
+ example: 'CNY'
3201
3377
  },
3202
- matchPayeePattern: {
3203
- type: 'object',
3204
- description: 'Payee matching pattern'
3378
+ warnings: {
3379
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3380
+ type: 'array',
3381
+ items: {
3382
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3383
+ }
3384
+ }
3385
+ },
3386
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3387
+ } as const;
3388
+
3389
+ export const $GenerateSnapshotBody = {
3390
+ type: 'object',
3391
+ properties: {}
3392
+ } as const;
3393
+
3394
+ export const $GenerateSnapshotResponse = {
3395
+ type: 'object',
3396
+ properties: {}
3397
+ } as const;
3398
+
3399
+ export const $BackfillSnapshotsBody = {
3400
+ type: 'object',
3401
+ properties: {}
3402
+ } as const;
3403
+
3404
+ export const $BackfillSnapshotsResponse = {
3405
+ type: 'object',
3406
+ properties: {}
3407
+ } as const;
3408
+
3409
+ export const $CreateBeanPriceDto = {
3410
+ type: 'object',
3411
+ properties: {
3412
+ currency: {
3413
+ type: 'string',
3414
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3415
+ example: 'USD'
3205
3416
  },
3206
- matchAmountTolerance: {
3417
+ quoteCurrency: {
3418
+ type: 'string',
3419
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3420
+ example: 'CNY'
3421
+ },
3422
+ amount: {
3207
3423
  type: 'number',
3208
- description: 'Amount tolerance percentage'
3424
+ description:
3425
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3426
+ example: 175.5,
3427
+ minimum: 0
3209
3428
  },
3210
- defaultExpenseAccount: {
3211
- type: 'object',
3212
- description: 'Default expense account'
3429
+ date: {
3430
+ type: 'string',
3431
+ description: 'Price date (ISO 8601 format)',
3432
+ example: '2024-11-05'
3213
3433
  },
3214
- defaultPaymentAccount: {
3434
+ metadata: {
3215
3435
  type: 'object',
3216
- description: 'Default payment account'
3436
+ description:
3437
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3438
+ example: {
3439
+ source: 'MANUAL',
3440
+ note: 'Bank valuation report',
3441
+ confidence: 0.95
3442
+ }
3443
+ }
3444
+ },
3445
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3446
+ } as const;
3447
+
3448
+ export const $PriceResponseDto = {
3449
+ type: 'object',
3450
+ properties: {
3451
+ id: {
3452
+ type: 'string',
3453
+ description: 'Unique identifier',
3454
+ example: 'uuid-123-456'
3217
3455
  },
3218
- defaultPayee: {
3219
- type: 'object',
3220
- description: 'Default payee'
3456
+ userId: {
3457
+ type: 'string',
3458
+ description: 'User ID (owner of the price)',
3459
+ example: 'user-123'
3221
3460
  },
3222
- isActive: {
3223
- type: 'boolean',
3224
- description: 'Whether rule is active'
3461
+ currency: {
3462
+ type: 'string',
3463
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3464
+ example: 'BTC'
3225
3465
  },
3226
- startDate: {
3466
+ quoteCurrency: {
3227
3467
  type: 'string',
3228
- description: 'Rule start date (YYYY-MM-DD)'
3468
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3469
+ example: 'USD'
3229
3470
  },
3230
- endDate: {
3231
- type: 'object',
3232
- description: 'Rule end date (YYYY-MM-DD)'
3471
+ amount: {
3472
+ type: 'number',
3473
+ description:
3474
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3475
+ example: 50000
3233
3476
  },
3234
- autoCreate: {
3235
- type: 'boolean',
3236
- description: 'Auto-create transaction on expected date'
3477
+ date: {
3478
+ type: 'string',
3479
+ description:
3480
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3481
+ example: '2024-01-01',
3482
+ format: 'date'
3237
3483
  },
3238
- lastOccurrence: {
3484
+ meta: {
3239
3485
  type: 'object',
3240
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3241
- },
3242
- totalCount: {
3243
- type: 'number',
3244
- description: 'Total matched transactions count'
3486
+ description:
3487
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3488
+ example: {
3489
+ source: 'MANUAL',
3490
+ note: 'User-defined price',
3491
+ confidence: 1
3492
+ }
3245
3493
  },
3246
3494
  createdAt: {
3247
3495
  format: 'date-time',
3248
3496
  type: 'string',
3249
- description: 'Created at timestamp'
3497
+ description: 'Creation timestamp',
3498
+ example: '2024-11-03T10:00:00Z'
3250
3499
  },
3251
3500
  updatedAt: {
3252
3501
  format: 'date-time',
3253
3502
  type: 'string',
3254
- description: 'Updated at timestamp'
3503
+ description: 'Last update timestamp',
3504
+ example: '2024-11-03T10:00:00Z'
3255
3505
  }
3256
3506
  },
3257
3507
  required: [
3258
3508
  'id',
3259
3509
  'userId',
3260
- 'name',
3261
- 'frequency',
3262
- 'expectedAmount',
3263
3510
  'currency',
3264
- 'matchAmountTolerance',
3265
- 'isActive',
3266
- 'startDate',
3267
- 'autoCreate',
3268
- 'totalCount',
3511
+ 'quoteCurrency',
3512
+ 'amount',
3513
+ 'date',
3514
+ 'meta',
3269
3515
  'createdAt',
3270
3516
  'updatedAt'
3271
3517
  ]
3272
3518
  } as const;
3273
3519
 
3274
- export const $CreateRuleFromTransactionDto = {
3520
+ export const $PriceListResponseDto = {
3275
3521
  type: 'object',
3276
3522
  properties: {
3277
- frequency: {
3278
- type: 'string',
3279
- description: 'Recurring frequency',
3280
- enum: [
3281
- 'WEEKLY',
3282
- 'BIWEEKLY',
3283
- 'MONTHLY',
3284
- 'BIMONTHLY',
3285
- 'QUARTERLY',
3286
- 'YEARLY',
3287
- 'CUSTOM'
3288
- ],
3289
- example: 'MONTHLY'
3290
- },
3291
- name: {
3292
- type: 'string',
3293
- description: 'Optional name override (default: transaction payee)',
3294
- maxLength: 100
3523
+ items: {
3524
+ description: 'List of prices',
3525
+ type: 'array',
3526
+ items: {
3527
+ $ref: '#/components/schemas/PriceResponseDto'
3528
+ }
3295
3529
  },
3296
- icon: {
3297
- type: 'string',
3298
- description: 'Optional icon emoji',
3299
- maxLength: 10
3530
+ total: {
3531
+ type: 'number',
3532
+ description: 'Total number of prices',
3533
+ example: 42
3300
3534
  }
3301
3535
  },
3302
- required: ['frequency']
3536
+ required: ['items', 'total']
3303
3537
  } as const;
3304
3538
 
3305
- export const $RecurringRuleWithStatsResponseDto = {
3539
+ export const $UpdateBeanPriceDto = {
3306
3540
  type: 'object',
3307
3541
  properties: {
3308
- id: {
3542
+ currency: {
3309
3543
  type: 'string',
3310
- description: 'Rule ID'
3544
+ description: 'Currency being priced'
3311
3545
  },
3312
- userId: {
3546
+ quoteCurrency: {
3313
3547
  type: 'string',
3314
- description: 'User ID'
3548
+ description: 'Quote currency (pricing currency)'
3315
3549
  },
3316
- name: {
3550
+ amount: {
3551
+ type: 'number',
3552
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3553
+ minimum: 0
3554
+ },
3555
+ date: {
3317
3556
  type: 'string',
3318
- description: 'Rule name'
3557
+ description: 'Price date (ISO 8601 format)'
3319
3558
  },
3320
- icon: {
3559
+ metadata: {
3321
3560
  type: 'object',
3322
- description: 'Icon emoji'
3323
- },
3324
- frequency: {
3561
+ description: 'Metadata'
3562
+ }
3563
+ }
3564
+ } as const;
3565
+
3566
+ export const $DeleteOwnUserDto = {
3567
+ type: 'object',
3568
+ properties: {
3569
+ accessToken: {
3325
3570
  type: 'string',
3326
- description: 'Recurring frequency'
3327
- },
3328
- expectedAmount: {
3329
- type: 'number',
3330
- description: 'Expected amount'
3571
+ description: 'Access token for user verification',
3572
+ example: 'abc123xyz'
3573
+ }
3574
+ },
3575
+ required: ['accessToken']
3576
+ } as const;
3577
+
3578
+ export const $UserSettingsResponseDto = {
3579
+ type: 'object',
3580
+ properties: {
3581
+ baseCurrency: {
3582
+ type: 'string',
3583
+ description:
3584
+ 'Stored base currency choice (ISO 4217) for net-worth/report aggregation. null = user never chose; aggregates fall back to the region default at display time (#713).',
3585
+ example: 'USD',
3586
+ nullable: true
3587
+ }
3588
+ },
3589
+ required: ['baseCurrency']
3590
+ } as const;
3591
+
3592
+ export const $UserResponseDto = {
3593
+ type: 'object',
3594
+ properties: {
3595
+ id: {
3596
+ type: 'string',
3597
+ description: 'User ID'
3331
3598
  },
3332
- expectedDay: {
3333
- type: 'object',
3334
- description: 'Expected day of month'
3599
+ role: {
3600
+ type: 'string',
3601
+ description: 'Assigned user role'
3335
3602
  },
3336
- customIntervalDays: {
3337
- type: 'object',
3338
- description: 'Custom interval in days'
3603
+ permissions: {
3604
+ description: 'Permission strings',
3605
+ type: 'array',
3606
+ items: {
3607
+ type: 'string'
3608
+ }
3339
3609
  },
3340
- currency: {
3610
+ settings: {
3611
+ description: 'User settings',
3612
+ allOf: [
3613
+ {
3614
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3615
+ }
3616
+ ]
3617
+ }
3618
+ },
3619
+ required: ['id', 'role', 'permissions', 'settings']
3620
+ } as const;
3621
+
3622
+ export const $SignupDto = {
3623
+ type: 'object',
3624
+ properties: {
3625
+ turnstileToken: {
3341
3626
  type: 'string',
3342
- description: 'Currency code'
3627
+ description:
3628
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3629
+ example: '0.abc123def456...'
3630
+ }
3631
+ }
3632
+ } as const;
3633
+
3634
+ export const $SignupResponseDto = {
3635
+ type: 'object',
3636
+ properties: {
3637
+ authToken: {
3638
+ type: 'string',
3639
+ description: 'JWT auth token',
3640
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3343
3641
  },
3344
- matchPayeePattern: {
3345
- type: 'object',
3346
- description: 'Payee matching pattern'
3642
+ accessToken: {
3643
+ type: 'string',
3644
+ description: 'Auto-generated access token'
3347
3645
  },
3348
- matchAmountTolerance: {
3646
+ role: {
3647
+ type: 'string',
3648
+ description: 'Assigned user role',
3649
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3650
+ }
3651
+ },
3652
+ required: ['authToken', 'accessToken', 'role']
3653
+ } as const;
3654
+
3655
+ export const $UpdateUserSettingDto = {
3656
+ type: 'object',
3657
+ properties: {
3658
+ secId: {
3349
3659
  type: 'number',
3350
- description: 'Amount tolerance percentage'
3660
+ description: 'Security ID'
3351
3661
  },
3352
- defaultExpenseAccount: {
3353
- type: 'object',
3354
- description: 'Default expense account'
3662
+ annualInterestRate: {
3663
+ type: 'number',
3664
+ description: 'Annual interest rate',
3665
+ example: 0.05
3355
3666
  },
3356
- defaultPaymentAccount: {
3357
- type: 'object',
3358
- description: 'Default payment account'
3667
+ currency: {
3668
+ type: 'string',
3669
+ description: 'Currency code',
3670
+ example: 'USD'
3359
3671
  },
3360
- defaultPayee: {
3361
- type: 'object',
3362
- description: 'Default payee'
3672
+ baseCurrency: {
3673
+ type: 'string',
3674
+ description: 'Base currency code',
3675
+ example: 'USD'
3363
3676
  },
3364
- isActive: {
3365
- type: 'boolean',
3366
- description: 'Whether rule is active'
3677
+ benchmark: {
3678
+ type: 'string',
3679
+ description: 'Benchmark symbol',
3680
+ example: 'SPY'
3367
3681
  },
3368
- startDate: {
3682
+ colorScheme: {
3369
3683
  type: 'string',
3370
- description: 'Rule start date (YYYY-MM-DD)'
3684
+ description: 'Color scheme',
3685
+ enum: ['DARK', 'LIGHT']
3371
3686
  },
3372
- endDate: {
3373
- type: 'object',
3374
- description: 'Rule end date (YYYY-MM-DD)'
3687
+ dateRange: {
3688
+ type: 'string',
3689
+ description: 'Date range filter',
3690
+ example: '1y'
3375
3691
  },
3376
- autoCreate: {
3377
- type: 'boolean',
3378
- description: 'Auto-create transaction on expected date'
3692
+ emergencyFund: {
3693
+ type: 'number',
3694
+ description: 'Emergency fund amount',
3695
+ example: 10000
3379
3696
  },
3380
- lastOccurrence: {
3381
- type: 'object',
3382
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3697
+ 'filters.accounts': {
3698
+ description: 'Account filter IDs',
3699
+ type: 'array',
3700
+ items: {
3701
+ type: 'string'
3702
+ }
3383
3703
  },
3384
- totalCount: {
3385
- type: 'number',
3386
- description: 'Total matched transactions count'
3704
+ 'filters.assetClasses': {
3705
+ description: 'Asset class filters',
3706
+ type: 'array',
3707
+ items: {
3708
+ type: 'string'
3709
+ }
3387
3710
  },
3388
- createdAt: {
3389
- format: 'date-time',
3711
+ 'filters.dataSource': {
3390
3712
  type: 'string',
3391
- description: 'Created at timestamp'
3713
+ description: 'Data source filter'
3392
3714
  },
3393
- updatedAt: {
3394
- format: 'date-time',
3715
+ 'filters.symbol': {
3395
3716
  type: 'string',
3396
- description: 'Updated at timestamp'
3717
+ description: 'Symbol filter'
3397
3718
  },
3398
- pendingCount: {
3399
- type: 'number',
3400
- description: 'Number of pending expected transactions'
3719
+ 'filters.tags': {
3720
+ description: 'Tag filters',
3721
+ type: 'array',
3722
+ items: {
3723
+ type: 'string'
3724
+ }
3401
3725
  },
3402
- overdueCount: {
3403
- type: 'number',
3404
- description: 'Number of overdue expected transactions'
3726
+ isExperimentalFeatures: {
3727
+ type: 'boolean',
3728
+ description: 'Enable experimental features'
3405
3729
  },
3406
- nextExpectedDate: {
3407
- type: 'object',
3408
- description: 'Next expected date (YYYY-MM-DD)'
3730
+ isRestrictedView: {
3731
+ type: 'boolean',
3732
+ description: 'Enable restricted view mode'
3409
3733
  },
3410
- totalAmount: {
3411
- type: 'number',
3412
- description: 'Total amount of all matched transactions'
3734
+ language: {
3735
+ type: 'string',
3736
+ description: 'Language code',
3737
+ example: 'en'
3413
3738
  },
3414
- averageAmount: {
3415
- type: 'number',
3416
- description: 'Average amount per transaction'
3739
+ locale: {
3740
+ type: 'string',
3741
+ description: 'Locale code',
3742
+ example: 'en-US'
3417
3743
  },
3418
- transactionCount: {
3744
+ projectedTotalAmount: {
3419
3745
  type: 'number',
3420
- description: 'Number of matched transactions'
3421
- },
3422
- firstDate: {
3423
- type: 'object',
3424
- description: 'First matched transaction date (YYYY-MM-DD)'
3746
+ description: 'Projected total amount',
3747
+ example: 1000000
3425
3748
  },
3426
- lastDate: {
3427
- type: 'object',
3428
- description: 'Last matched transaction date (YYYY-MM-DD)'
3749
+ retirementDate: {
3750
+ type: 'string',
3751
+ description: 'Retirement date in ISO 8601 format',
3752
+ example: '2050-01-01'
3429
3753
  },
3430
- variance: {
3754
+ savingsRate: {
3431
3755
  type: 'number',
3432
- description: 'Amount variance (standard deviation squared)'
3756
+ description: 'Savings rate percentage',
3757
+ example: 0.2
3433
3758
  },
3434
- upcomingCount: {
3435
- type: 'number',
3436
- description: 'Number of upcoming expected transactions'
3437
- }
3438
- },
3439
- required: [
3440
- 'id',
3441
- 'userId',
3442
- 'name',
3443
- 'frequency',
3444
- 'expectedAmount',
3445
- 'currency',
3446
- 'matchAmountTolerance',
3447
- 'isActive',
3448
- 'startDate',
3449
- 'autoCreate',
3450
- 'totalCount',
3451
- 'createdAt',
3452
- 'updatedAt',
3453
- 'pendingCount',
3454
- 'overdueCount',
3455
- 'totalAmount',
3456
- 'averageAmount',
3457
- 'transactionCount',
3458
- 'variance',
3459
- 'upcomingCount'
3460
- ]
3759
+ viewMode: {
3760
+ type: 'string',
3761
+ description: 'View mode',
3762
+ enum: ['DEFAULT', 'ZEN']
3763
+ }
3764
+ }
3461
3765
  } as const;
3462
3766
 
3463
- export const $UpdateRecurringRuleDto = {
3767
+ export const $UpdatePropertyDto = {
3768
+ type: 'object',
3769
+ properties: {
3770
+ value: {
3771
+ type: 'string',
3772
+ description: 'Property value'
3773
+ }
3774
+ },
3775
+ required: ['value']
3776
+ } as const;
3777
+
3778
+ export const $CreateRecurringRuleDto = {
3464
3779
  type: 'object',
3465
3780
  properties: {
3466
3781
  name: {
3467
3782
  type: 'string',
3468
- description: 'Rule name',
3783
+ description: 'Rule name (unique per user)',
3469
3784
  maxLength: 100
3470
3785
  },
3471
3786
  icon: {
@@ -3488,7 +3803,7 @@ export const $UpdateRecurringRuleDto = {
3488
3803
  },
3489
3804
  expectedAmount: {
3490
3805
  type: 'number',
3491
- description: 'Expected amount',
3806
+ description: 'Expected amount (positive number)',
3492
3807
  minimum: 0
3493
3808
  },
3494
3809
  expectedDay: {
@@ -3499,7 +3814,7 @@ export const $UpdateRecurringRuleDto = {
3499
3814
  },
3500
3815
  customIntervalDays: {
3501
3816
  type: 'number',
3502
- description: 'Custom interval in days',
3817
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3503
3818
  minimum: 1
3504
3819
  },
3505
3820
  currency: {
@@ -3509,118 +3824,136 @@ export const $UpdateRecurringRuleDto = {
3509
3824
  },
3510
3825
  matchPayeePattern: {
3511
3826
  type: 'string',
3512
- description: 'Payee matching pattern',
3827
+ description: 'Payee matching pattern (supports wildcards)',
3513
3828
  maxLength: 200
3514
3829
  },
3515
3830
  matchAmountTolerance: {
3516
3831
  type: 'number',
3517
3832
  description: 'Amount tolerance percentage (0-1)',
3833
+ default: 0.075,
3518
3834
  minimum: 0,
3519
3835
  maximum: 1
3520
3836
  },
3521
3837
  defaultExpenseAccount: {
3522
3838
  type: 'string',
3523
- description: 'Default expense account',
3839
+ description: 'Default expense account for auto-create',
3524
3840
  maxLength: 200
3525
3841
  },
3526
3842
  defaultPaymentAccount: {
3527
3843
  type: 'string',
3528
- description: 'Default payment account',
3844
+ description: 'Default payment account for auto-create',
3529
3845
  maxLength: 200
3530
3846
  },
3531
3847
  defaultPayee: {
3532
3848
  type: 'string',
3533
- description: 'Default payee',
3849
+ description: 'Default payee for auto-create',
3534
3850
  maxLength: 200
3535
3851
  },
3536
3852
  autoCreate: {
3537
3853
  type: 'boolean',
3538
- description: 'Auto-create transaction'
3854
+ description: 'Auto-create transaction when expected date arrives',
3855
+ default: false
3539
3856
  },
3540
- isActive: {
3541
- type: 'boolean',
3542
- description: 'Rule active status'
3857
+ startDate: {
3858
+ type: 'string',
3859
+ description: 'Rule start date (ISO format)'
3543
3860
  },
3544
3861
  endDate: {
3545
3862
  type: 'string',
3546
3863
  description: 'Rule end date (ISO format)'
3547
3864
  }
3548
- }
3865
+ },
3866
+ required: [
3867
+ 'name',
3868
+ 'frequency',
3869
+ 'expectedAmount',
3870
+ 'matchAmountTolerance',
3871
+ 'autoCreate'
3872
+ ]
3549
3873
  } as const;
3550
3874
 
3551
- export const $ExpectedTransactionRuleDto = {
3875
+ export const $RecurringRuleResponseDto = {
3552
3876
  type: 'object',
3553
3877
  properties: {
3878
+ id: {
3879
+ type: 'string',
3880
+ description: 'Rule ID'
3881
+ },
3882
+ userId: {
3883
+ type: 'string',
3884
+ description: 'User ID'
3885
+ },
3554
3886
  name: {
3555
3887
  type: 'string',
3556
3888
  description: 'Rule name'
3557
3889
  },
3558
3890
  icon: {
3559
- type: 'object',
3560
- description: 'Rule icon'
3891
+ type: 'string',
3892
+ description: 'Icon emoji'
3561
3893
  },
3562
3894
  frequency: {
3563
3895
  type: 'string',
3564
- description: 'Rule frequency'
3896
+ description: 'Recurring frequency'
3897
+ },
3898
+ expectedAmount: {
3899
+ type: 'number',
3900
+ description: 'Expected amount'
3901
+ },
3902
+ expectedDay: {
3903
+ type: 'number',
3904
+ description: 'Expected day of month'
3905
+ },
3906
+ customIntervalDays: {
3907
+ type: 'number',
3908
+ description: 'Custom interval in days'
3565
3909
  },
3566
3910
  currency: {
3567
3911
  type: 'string',
3568
3912
  description: 'Currency code'
3569
- }
3570
- },
3571
- required: ['name', 'frequency', 'currency']
3572
- } as const;
3573
-
3574
- export const $ExpectedTransactionResponseDto = {
3575
- type: 'object',
3576
- properties: {
3577
- id: {
3578
- type: 'string',
3579
- description: 'Expected transaction ID'
3580
3913
  },
3581
- userId: {
3914
+ matchPayeePattern: {
3582
3915
  type: 'string',
3583
- description: 'User ID'
3916
+ description: 'Payee matching pattern'
3584
3917
  },
3585
- ruleId: {
3586
- type: 'string',
3587
- description: 'Associated rule ID'
3918
+ matchAmountTolerance: {
3919
+ type: 'number',
3920
+ description: 'Amount tolerance percentage'
3588
3921
  },
3589
- expectedDate: {
3922
+ defaultExpenseAccount: {
3590
3923
  type: 'string',
3591
- description: 'Expected date (YYYY-MM-DD)'
3924
+ description: 'Default expense account'
3592
3925
  },
3593
- expectedAmount: {
3594
- type: 'number',
3595
- description: 'Expected amount'
3926
+ defaultPaymentAccount: {
3927
+ type: 'string',
3928
+ description: 'Default payment account'
3596
3929
  },
3597
- status: {
3930
+ defaultPayee: {
3598
3931
  type: 'string',
3599
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3932
+ description: 'Default payee'
3600
3933
  },
3601
- matchedTransactionId: {
3602
- type: 'object',
3603
- description: 'Matched transaction ID'
3934
+ isActive: {
3935
+ type: 'boolean',
3936
+ description: 'Whether rule is active'
3604
3937
  },
3605
- matchedAt: {
3606
- type: 'object',
3607
- description: 'Match timestamp (ISO 8601)'
3938
+ startDate: {
3939
+ type: 'string',
3940
+ description: 'Rule start date (YYYY-MM-DD)'
3608
3941
  },
3609
- matchConfidence: {
3610
- type: 'object',
3611
- description: 'Match confidence score (0-1)'
3942
+ endDate: {
3943
+ type: 'string',
3944
+ description: 'Rule end date (YYYY-MM-DD)'
3612
3945
  },
3613
- isOverdue: {
3946
+ autoCreate: {
3614
3947
  type: 'boolean',
3615
- description: 'Whether this expected transaction is overdue'
3948
+ description: 'Auto-create transaction on expected date'
3616
3949
  },
3617
- rule: {
3618
- description: 'Rule information',
3619
- allOf: [
3620
- {
3621
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3622
- }
3623
- ]
3950
+ lastOccurrence: {
3951
+ type: 'string',
3952
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3953
+ },
3954
+ totalCount: {
3955
+ type: 'number',
3956
+ description: 'Total matched transactions count'
3624
3957
  },
3625
3958
  createdAt: {
3626
3959
  format: 'date-time',
@@ -3636,647 +3969,581 @@ export const $ExpectedTransactionResponseDto = {
3636
3969
  required: [
3637
3970
  'id',
3638
3971
  'userId',
3639
- 'ruleId',
3640
- 'expectedDate',
3972
+ 'name',
3973
+ 'frequency',
3641
3974
  'expectedAmount',
3642
- 'status',
3643
- 'isOverdue',
3644
- 'rule',
3975
+ 'currency',
3976
+ 'matchAmountTolerance',
3977
+ 'isActive',
3978
+ 'startDate',
3979
+ 'autoCreate',
3980
+ 'totalCount',
3645
3981
  'createdAt',
3646
3982
  'updatedAt'
3647
3983
  ]
3648
3984
  } as const;
3649
3985
 
3650
- export const $ExpectedTransactionListResponseDto = {
3986
+ export const $CreateRuleFromTransactionDto = {
3651
3987
  type: 'object',
3652
3988
  properties: {
3653
- items: {
3654
- type: 'array',
3655
- items: {
3656
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3657
- }
3989
+ frequency: {
3990
+ type: 'string',
3991
+ description: 'Recurring frequency',
3992
+ enum: [
3993
+ 'WEEKLY',
3994
+ 'BIWEEKLY',
3995
+ 'MONTHLY',
3996
+ 'BIMONTHLY',
3997
+ 'QUARTERLY',
3998
+ 'YEARLY',
3999
+ 'CUSTOM'
4000
+ ],
4001
+ example: 'MONTHLY'
3658
4002
  },
3659
- total: {
3660
- type: 'number',
3661
- description: 'Total count'
3662
- }
3663
- },
3664
- required: ['items', 'total']
3665
- } as const;
3666
-
3667
- export const $ConfirmMatchDto = {
3668
- type: 'object',
3669
- properties: {
3670
- transactionId: {
4003
+ name: {
3671
4004
  type: 'string',
3672
- description: 'Transaction ID to match with'
4005
+ description: 'Optional name override (default: transaction payee)',
4006
+ maxLength: 100
4007
+ },
4008
+ icon: {
4009
+ type: 'string',
4010
+ description: 'Optional icon emoji',
4011
+ maxLength: 10
3673
4012
  }
3674
4013
  },
3675
- required: ['transactionId']
4014
+ required: ['frequency']
3676
4015
  } as const;
3677
4016
 
3678
- export const $EnterNowDto = {
4017
+ export const $RecurringRuleWithStatsResponseDto = {
3679
4018
  type: 'object',
3680
4019
  properties: {
3681
- expenseAccount: {
4020
+ id: {
3682
4021
  type: 'string',
3683
- description:
3684
- 'Override expense account (uses rule default if not provided)',
3685
- maxLength: 200
4022
+ description: 'Rule ID'
3686
4023
  },
3687
- paymentAccount: {
4024
+ userId: {
3688
4025
  type: 'string',
3689
- description:
3690
- 'Override payment account (uses rule default if not provided)',
3691
- maxLength: 200
4026
+ description: 'User ID'
3692
4027
  },
3693
- amount: {
4028
+ name: {
4029
+ type: 'string',
4030
+ description: 'Rule name'
4031
+ },
4032
+ icon: {
4033
+ type: 'string',
4034
+ description: 'Icon emoji'
4035
+ },
4036
+ frequency: {
4037
+ type: 'string',
4038
+ description: 'Recurring frequency'
4039
+ },
4040
+ expectedAmount: {
3694
4041
  type: 'number',
3695
- description: 'Override amount (uses expected amount if not provided)',
3696
- minimum: 0
4042
+ description: 'Expected amount'
4043
+ },
4044
+ expectedDay: {
4045
+ type: 'number',
4046
+ description: 'Expected day of month'
4047
+ },
4048
+ customIntervalDays: {
4049
+ type: 'number',
4050
+ description: 'Custom interval in days'
4051
+ },
4052
+ currency: {
4053
+ type: 'string',
4054
+ description: 'Currency code'
4055
+ },
4056
+ matchPayeePattern: {
4057
+ type: 'string',
4058
+ description: 'Payee matching pattern'
4059
+ },
4060
+ matchAmountTolerance: {
4061
+ type: 'number',
4062
+ description: 'Amount tolerance percentage'
4063
+ },
4064
+ defaultExpenseAccount: {
4065
+ type: 'string',
4066
+ description: 'Default expense account'
3697
4067
  },
3698
- payee: {
4068
+ defaultPaymentAccount: {
3699
4069
  type: 'string',
3700
- description: 'Override payee (uses rule default if not provided)',
3701
- maxLength: 200
4070
+ description: 'Default payment account'
3702
4071
  },
3703
- narration: {
4072
+ defaultPayee: {
3704
4073
  type: 'string',
3705
- description: 'Optional narration',
3706
- maxLength: 500
3707
- }
3708
- }
3709
- } as const;
3710
-
3711
- export const $ForecastItemDto = {
3712
- type: 'object',
3713
- properties: {
3714
- rule: {
4074
+ description: 'Default payee'
4075
+ },
4076
+ isActive: {
4077
+ type: 'boolean',
4078
+ description: 'Whether rule is active'
4079
+ },
4080
+ startDate: {
3715
4081
  type: 'string',
3716
- description: 'Rule name',
3717
- example: 'Rent'
4082
+ description: 'Rule start date (YYYY-MM-DD)'
3718
4083
  },
3719
- ruleId: {
4084
+ endDate: {
3720
4085
  type: 'string',
3721
- description: 'Rule ID',
3722
- example: 'clx123...'
4086
+ description: 'Rule end date (YYYY-MM-DD)'
3723
4087
  },
3724
- amount: {
3725
- type: 'number',
3726
- description: 'Expected amount',
3727
- example: 3000
4088
+ autoCreate: {
4089
+ type: 'boolean',
4090
+ description: 'Auto-create transaction on expected date'
3728
4091
  },
3729
- date: {
4092
+ lastOccurrence: {
3730
4093
  type: 'string',
3731
- description: 'Expected date (YYYY-MM-DD)',
3732
- example: '2024-04-01'
4094
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3733
4095
  },
3734
- icon: {
3735
- type: 'string',
3736
- description: 'Rule icon emoji',
3737
- example: '🏠',
3738
- nullable: true
4096
+ totalCount: {
4097
+ type: 'number',
4098
+ description: 'Total matched transactions count'
3739
4099
  },
3740
- currency: {
4100
+ createdAt: {
4101
+ format: 'date-time',
3741
4102
  type: 'string',
3742
- description: 'Currency code',
3743
- example: 'CNY'
3744
- }
3745
- },
3746
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3747
- } as const;
3748
-
3749
- export const $MonthlyForecastDto = {
3750
- type: 'object',
3751
- properties: {
3752
- month: {
4103
+ description: 'Created at timestamp'
4104
+ },
4105
+ updatedAt: {
4106
+ format: 'date-time',
3753
4107
  type: 'string',
3754
- description: 'Month (YYYY-MM)',
3755
- example: '2024-04'
4108
+ description: 'Updated at timestamp'
3756
4109
  },
3757
- expectedOutflow: {
4110
+ pendingCount: {
3758
4111
  type: 'number',
3759
- description: 'Total expected outflow for the month',
3760
- example: 8500
4112
+ description: 'Number of pending expected transactions'
3761
4113
  },
3762
- itemCount: {
4114
+ overdueCount: {
3763
4115
  type: 'number',
3764
- description: 'Number of expected transactions',
3765
- example: 3
3766
- },
3767
- byCurrency: {
3768
- type: 'object',
3769
- description: 'Breakdown by currency',
3770
- example: {
3771
- CNY: 8500,
3772
- USD: 100
3773
- }
4116
+ description: 'Number of overdue expected transactions'
3774
4117
  },
3775
- items: {
3776
- description: 'Individual forecast items',
3777
- type: 'array',
3778
- items: {
3779
- $ref: '#/components/schemas/ForecastItemDto'
3780
- }
3781
- }
3782
- },
3783
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3784
- } as const;
3785
-
3786
- export const $ForecastResponseDto = {
3787
- type: 'object',
3788
- properties: {
3789
- forecast: {
3790
- description: 'Monthly forecast data',
3791
- type: 'array',
3792
- items: {
3793
- $ref: '#/components/schemas/MonthlyForecastDto'
3794
- }
4118
+ nextExpectedDate: {
4119
+ type: 'string',
4120
+ description: 'Next expected date (YYYY-MM-DD)'
3795
4121
  },
3796
- totalOutflow: {
4122
+ totalAmount: {
3797
4123
  type: 'number',
3798
- description: 'Total expected outflow across all months',
3799
- example: 25500
4124
+ description: 'Total amount of all matched transactions'
3800
4125
  },
3801
- totalByCurrency: {
3802
- type: 'object',
3803
- description: 'Total by currency across all months',
3804
- example: {
3805
- CNY: 25500,
3806
- USD: 300
3807
- }
4126
+ averageAmount: {
4127
+ type: 'number',
4128
+ description: 'Average amount per transaction'
3808
4129
  },
3809
- rulesCount: {
4130
+ transactionCount: {
3810
4131
  type: 'number',
3811
- description: 'Number of active recurring rules included',
3812
- example: 5
4132
+ description: 'Number of matched transactions'
3813
4133
  },
3814
- periodStart: {
4134
+ firstDate: {
3815
4135
  type: 'string',
3816
- description: 'Forecast period start date',
3817
- example: '2024-04-01'
4136
+ description: 'First matched transaction date (YYYY-MM-DD)'
3818
4137
  },
3819
- periodEnd: {
4138
+ lastDate: {
3820
4139
  type: 'string',
3821
- description: 'Forecast period end date',
3822
- example: '2024-06-30'
4140
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4141
+ },
4142
+ variance: {
4143
+ type: 'number',
4144
+ description: 'Amount variance (standard deviation squared)'
4145
+ },
4146
+ upcomingCount: {
4147
+ type: 'number',
4148
+ description: 'Number of upcoming expected transactions'
3823
4149
  }
3824
4150
  },
3825
4151
  required: [
3826
- 'forecast',
3827
- 'totalOutflow',
3828
- 'totalByCurrency',
3829
- 'rulesCount',
3830
- 'periodStart',
3831
- 'periodEnd'
4152
+ 'id',
4153
+ 'userId',
4154
+ 'name',
4155
+ 'frequency',
4156
+ 'expectedAmount',
4157
+ 'currency',
4158
+ 'matchAmountTolerance',
4159
+ 'isActive',
4160
+ 'startDate',
4161
+ 'autoCreate',
4162
+ 'totalCount',
4163
+ 'createdAt',
4164
+ 'updatedAt',
4165
+ 'pendingCount',
4166
+ 'overdueCount',
4167
+ 'totalAmount',
4168
+ 'averageAmount',
4169
+ 'transactionCount',
4170
+ 'variance',
4171
+ 'upcomingCount'
3832
4172
  ]
3833
4173
  } as const;
3834
4174
 
3835
- export const $CurrencyBalanceDto = {
4175
+ export const $UpdateRecurringRuleDto = {
3836
4176
  type: 'object',
3837
4177
  properties: {
3838
- currency: {
4178
+ name: {
3839
4179
  type: 'string',
3840
- description: 'ISO 4217 currency code',
3841
- example: 'CNY'
4180
+ description: 'Rule name (unique per user)',
4181
+ maxLength: 100
3842
4182
  },
3843
- balance: {
3844
- type: 'string',
3845
- description: 'Balance amount',
3846
- example: '500000.00'
3847
- }
3848
- },
3849
- required: ['currency', 'balance']
3850
- } as const;
3851
-
3852
- export const $TimeSeriesPointDto = {
3853
- type: 'object',
3854
- properties: {
3855
- date: {
4183
+ icon: {
3856
4184
  type: 'string',
3857
- description: 'Date in YYYY-MM-DD format',
3858
- example: '2024-06-15'
4185
+ description: 'Icon emoji',
4186
+ maxLength: 10
3859
4187
  },
3860
- value: {
4188
+ frequency: {
3861
4189
  type: 'string',
3862
- description: 'Value at this date (in base currency)',
3863
- example: '500000.00'
3864
- },
3865
- change: {
3866
- type: 'object',
3867
- description: 'Change from previous point',
3868
- example: '5000.00'
4190
+ description: 'Recurring frequency',
4191
+ enum: [
4192
+ 'WEEKLY',
4193
+ 'BIWEEKLY',
4194
+ 'MONTHLY',
4195
+ 'BIMONTHLY',
4196
+ 'QUARTERLY',
4197
+ 'YEARLY',
4198
+ 'CUSTOM'
4199
+ ]
3869
4200
  },
3870
- assets: {
3871
- type: 'string',
3872
- description: 'Total assets at this date (in base currency)',
3873
- example: '494338.00'
4201
+ expectedAmount: {
4202
+ type: 'number',
4203
+ description: 'Expected amount (positive number)',
4204
+ minimum: 0
3874
4205
  },
3875
- liabilities: {
3876
- type: 'string',
3877
- description: 'Total liabilities at this date (in base currency)',
3878
- example: '310098.00'
4206
+ expectedDay: {
4207
+ type: 'number',
4208
+ description: 'Expected day of month (1-31)',
4209
+ minimum: 1,
4210
+ maximum: 31
3879
4211
  },
3880
- byCurrency: {
3881
- description: 'Multi-currency breakdown for this point',
3882
- type: 'array',
3883
- items: {
3884
- $ref: '#/components/schemas/CurrencyBalanceDto'
3885
- }
3886
- }
3887
- },
3888
- required: ['date', 'value']
3889
- } as const;
3890
-
3891
- export const $TrendSummaryDto = {
3892
- type: 'object',
3893
- properties: {
3894
- startValue: {
4212
+ currency: {
3895
4213
  type: 'string',
3896
- description: 'Value at start of period',
3897
- example: '450000.00'
4214
+ description: 'Currency code',
4215
+ maxLength: 10
3898
4216
  },
3899
- endValue: {
4217
+ matchPayeePattern: {
3900
4218
  type: 'string',
3901
- description: 'Value at end of period',
3902
- example: '500000.00'
4219
+ description: 'Payee matching pattern (supports wildcards)',
4220
+ maxLength: 200
3903
4221
  },
3904
- totalChange: {
4222
+ matchAmountTolerance: {
4223
+ type: 'number',
4224
+ description: 'Amount tolerance percentage (0-1)',
4225
+ default: 0.075,
4226
+ minimum: 0,
4227
+ maximum: 1
4228
+ },
4229
+ defaultExpenseAccount: {
3905
4230
  type: 'string',
3906
- description: 'Total change over period',
3907
- example: '50000.00'
4231
+ description: 'Default expense account for auto-create',
4232
+ maxLength: 200
3908
4233
  },
3909
- totalChangePercentage: {
4234
+ defaultPaymentAccount: {
3910
4235
  type: 'string',
3911
- description: 'Total change percentage',
3912
- example: '+11.11%'
3913
- }
3914
- },
3915
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3916
- } as const;
3917
-
3918
- export const $MultiCurrencyPointDto = {
3919
- type: 'object',
3920
- properties: {
3921
- date: {
4236
+ description: 'Default payment account for auto-create',
4237
+ maxLength: 200
4238
+ },
4239
+ defaultPayee: {
3922
4240
  type: 'string',
3923
- description: 'Date in YYYY-MM-DD format',
3924
- example: '2024-06-15'
4241
+ description: 'Default payee for auto-create',
4242
+ maxLength: 200
3925
4243
  },
3926
- byCurrency: {
3927
- description: 'Balances by currency',
3928
- type: 'array',
3929
- items: {
3930
- $ref: '#/components/schemas/CurrencyBalanceDto'
3931
- }
4244
+ autoCreate: {
4245
+ type: 'boolean',
4246
+ description: 'Auto-create transaction when expected date arrives',
4247
+ default: false
4248
+ },
4249
+ endDate: {
4250
+ type: 'string',
4251
+ description: 'Rule end date (ISO format)'
4252
+ },
4253
+ customIntervalDays: {
4254
+ type: 'number',
4255
+ description: 'Custom interval in days',
4256
+ minimum: 1
4257
+ },
4258
+ isActive: {
4259
+ type: 'boolean',
4260
+ description: 'Rule active status'
3932
4261
  }
3933
- },
3934
- required: ['date', 'byCurrency']
4262
+ }
3935
4263
  } as const;
3936
4264
 
3937
- export const $PortfolioTrendsResponseDto = {
4265
+ export const $ExpectedTransactionRuleDto = {
3938
4266
  type: 'object',
3939
4267
  properties: {
3940
- series: {
3941
- description: 'Time series data points',
3942
- type: 'array',
3943
- items: {
3944
- $ref: '#/components/schemas/TimeSeriesPointDto'
3945
- }
3946
- },
3947
- summary: {
3948
- description: 'Period summary',
3949
- allOf: [
3950
- {
3951
- $ref: '#/components/schemas/TrendSummaryDto'
3952
- }
3953
- ]
4268
+ name: {
4269
+ type: 'string',
4270
+ description: 'Rule name'
3954
4271
  },
3955
- period: {
4272
+ icon: {
3956
4273
  type: 'string',
3957
- description: 'Period requested',
3958
- example: '6m'
4274
+ description: 'Rule icon'
3959
4275
  },
3960
- granularity: {
4276
+ frequency: {
3961
4277
  type: 'string',
3962
- description: 'Data granularity',
3963
- example: 'month'
4278
+ description: 'Rule frequency'
3964
4279
  },
3965
4280
  currency: {
3966
4281
  type: 'string',
3967
- description: 'Base currency for converted values',
3968
- example: 'CNY'
3969
- },
3970
- byCurrency: {
3971
- description:
3972
- 'Multi-currency time series (each point has currency breakdown)',
3973
- type: 'array',
3974
- items: {
3975
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3976
- }
3977
- },
3978
- warnings: {
3979
- description: 'Exchange rate warnings',
3980
- type: 'array',
3981
- items: {
3982
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3983
- }
4282
+ description: 'Currency code'
3984
4283
  }
3985
4284
  },
3986
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4285
+ required: ['name', 'frequency', 'currency']
3987
4286
  } as const;
3988
4287
 
3989
- export const $CashFlowPointDto = {
4288
+ export const $ExpectedTransactionResponseDto = {
3990
4289
  type: 'object',
3991
4290
  properties: {
3992
- month: {
4291
+ id: {
3993
4292
  type: 'string',
3994
- description: 'Month key (YYYY-MM)',
3995
- example: '2024-03'
4293
+ description: 'Expected transaction ID'
3996
4294
  },
3997
- income: {
4295
+ userId: {
3998
4296
  type: 'string',
3999
- description: 'Income in base currency (absolute, converted)',
4000
- example: '10000.00'
4297
+ description: 'User ID'
4001
4298
  },
4002
- expense: {
4299
+ ruleId: {
4003
4300
  type: 'string',
4004
- description: 'Expense in base currency (absolute, converted)',
4005
- example: '5000.00'
4301
+ description: 'Associated rule ID'
4006
4302
  },
4007
- netSavings: {
4008
- type: 'string',
4009
- description: 'netSavings = income − expense (savings positive)',
4010
- example: '5000.00'
4011
- }
4012
- },
4013
- required: ['month', 'income', 'expense', 'netSavings']
4014
- } as const;
4015
-
4016
- export const $CashFlowTrendSummaryDto = {
4017
- type: 'object',
4018
- properties: {
4019
- totalIncome: {
4303
+ expectedDate: {
4020
4304
  type: 'string',
4021
- description: 'Total income across the period',
4022
- example: '60000.00'
4305
+ description: 'Expected date (YYYY-MM-DD)'
4023
4306
  },
4024
- totalExpense: {
4307
+ expectedAmount: {
4308
+ type: 'number',
4309
+ description: 'Expected amount'
4310
+ },
4311
+ status: {
4025
4312
  type: 'string',
4026
- description: 'Total expense across the period',
4027
- example: '30000.00'
4313
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4028
4314
  },
4029
- totalNetSavings: {
4315
+ matchedTransactionId: {
4030
4316
  type: 'string',
4031
- description: 'income − expense across the period',
4032
- example: '30000.00'
4317
+ description: 'Matched transaction ID'
4033
4318
  },
4034
- averageMonthlyNetSavings: {
4319
+ matchedAt: {
4035
4320
  type: 'string',
4036
- description:
4037
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
4038
- example: '5000.00'
4039
- }
4040
- },
4041
- required: [
4042
- 'totalIncome',
4043
- 'totalExpense',
4044
- 'totalNetSavings',
4045
- 'averageMonthlyNetSavings'
4046
- ]
4047
- } as const;
4048
-
4049
- export const $CashFlowTrendsResponseDto = {
4050
- type: 'object',
4051
- properties: {
4052
- series: {
4053
- description:
4054
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4055
- type: 'array',
4056
- items: {
4057
- $ref: '#/components/schemas/CashFlowPointDto'
4058
- }
4321
+ description: 'Match timestamp (ISO 8601)'
4059
4322
  },
4060
- summary: {
4061
- description: 'Period totals',
4323
+ matchConfidence: {
4324
+ type: 'number',
4325
+ description: 'Match confidence score (0-1)'
4326
+ },
4327
+ isOverdue: {
4328
+ type: 'boolean',
4329
+ description: 'Whether this expected transaction is overdue'
4330
+ },
4331
+ rule: {
4332
+ description: 'Rule information',
4062
4333
  allOf: [
4063
4334
  {
4064
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4335
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4065
4336
  }
4066
4337
  ]
4067
4338
  },
4068
- period: {
4069
- type: 'string',
4070
- description: 'Period requested',
4071
- example: '6m'
4072
- },
4073
- granularity: {
4339
+ createdAt: {
4340
+ format: 'date-time',
4074
4341
  type: 'string',
4075
- description: 'Data granularity (v1 returns month buckets)',
4076
- example: 'month'
4342
+ description: 'Created at timestamp'
4077
4343
  },
4078
- currency: {
4344
+ updatedAt: {
4345
+ format: 'date-time',
4079
4346
  type: 'string',
4080
- description: 'Base currency for converted values',
4081
- example: 'CNY'
4082
- },
4083
- warnings: {
4084
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4085
- type: 'array',
4086
- items: {
4087
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4088
- }
4347
+ description: 'Updated at timestamp'
4089
4348
  }
4090
4349
  },
4091
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4092
- } as const;
4093
-
4094
- export const $GenerateSnapshotBody = {
4095
- type: 'object',
4096
- properties: {}
4097
- } as const;
4098
-
4099
- export const $GenerateSnapshotResponse = {
4100
- type: 'object',
4101
- properties: {}
4102
- } as const;
4103
-
4104
- export const $BackfillSnapshotsBody = {
4105
- type: 'object',
4106
- properties: {}
4107
- } as const;
4108
-
4109
- export const $BackfillSnapshotsResponse = {
4110
- type: 'object',
4111
- properties: {}
4350
+ required: [
4351
+ 'id',
4352
+ 'userId',
4353
+ 'ruleId',
4354
+ 'expectedDate',
4355
+ 'expectedAmount',
4356
+ 'status',
4357
+ 'isOverdue',
4358
+ 'rule',
4359
+ 'createdAt',
4360
+ 'updatedAt'
4361
+ ]
4112
4362
  } as const;
4113
4363
 
4114
- export const $DeleteOwnUserDto = {
4364
+ export const $ExpectedTransactionListResponseDto = {
4115
4365
  type: 'object',
4116
4366
  properties: {
4117
- accessToken: {
4118
- type: 'string',
4119
- description: 'Access token for user verification',
4120
- example: 'abc123xyz'
4367
+ items: {
4368
+ type: 'array',
4369
+ items: {
4370
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4371
+ }
4372
+ },
4373
+ total: {
4374
+ type: 'number',
4375
+ description: 'Total count'
4121
4376
  }
4122
4377
  },
4123
- required: ['accessToken']
4378
+ required: ['items', 'total']
4124
4379
  } as const;
4125
4380
 
4126
- export const $SignupDto = {
4381
+ export const $ConfirmMatchDto = {
4127
4382
  type: 'object',
4128
4383
  properties: {
4129
- turnstileToken: {
4384
+ transactionId: {
4130
4385
  type: 'string',
4131
- description:
4132
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4133
- example: '0.abc123def456...'
4386
+ description: 'Transaction ID to match with'
4134
4387
  }
4135
- }
4388
+ },
4389
+ required: ['transactionId']
4136
4390
  } as const;
4137
4391
 
4138
- export const $SignupResponseDto = {
4392
+ export const $EnterNowDto = {
4139
4393
  type: 'object',
4140
4394
  properties: {
4141
- authToken: {
4395
+ expenseAccount: {
4142
4396
  type: 'string',
4143
- description: 'JWT auth token',
4144
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4397
+ description:
4398
+ 'Override expense account (uses rule default if not provided)',
4399
+ maxLength: 200
4145
4400
  },
4146
- accessToken: {
4401
+ paymentAccount: {
4147
4402
  type: 'string',
4148
- description: 'Auto-generated access token'
4403
+ description:
4404
+ 'Override payment account (uses rule default if not provided)',
4405
+ maxLength: 200
4149
4406
  },
4150
- role: {
4407
+ amount: {
4408
+ type: 'number',
4409
+ description: 'Override amount (uses expected amount if not provided)',
4410
+ minimum: 0
4411
+ },
4412
+ payee: {
4151
4413
  type: 'string',
4152
- description: 'Assigned user role',
4153
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4414
+ description: 'Override payee (uses rule default if not provided)',
4415
+ maxLength: 200
4416
+ },
4417
+ narration: {
4418
+ type: 'string',
4419
+ description: 'Optional narration',
4420
+ maxLength: 500
4154
4421
  }
4155
- },
4156
- required: ['authToken', 'accessToken', 'role']
4422
+ }
4157
4423
  } as const;
4158
4424
 
4159
- export const $UpdateUserSettingDto = {
4425
+ export const $ForecastItemDto = {
4160
4426
  type: 'object',
4161
4427
  properties: {
4162
- secId: {
4163
- type: 'number',
4164
- description: 'Security ID'
4428
+ rule: {
4429
+ type: 'string',
4430
+ description: 'Rule name',
4431
+ example: 'Rent'
4165
4432
  },
4166
- annualInterestRate: {
4433
+ ruleId: {
4434
+ type: 'string',
4435
+ description: 'Rule ID',
4436
+ example: 'clx123...'
4437
+ },
4438
+ amount: {
4167
4439
  type: 'number',
4168
- description: 'Annual interest rate',
4169
- example: 0.05
4440
+ description: 'Expected amount',
4441
+ example: 3000
4170
4442
  },
4171
- currency: {
4443
+ date: {
4172
4444
  type: 'string',
4173
- description: 'Currency code',
4174
- example: 'USD'
4445
+ description: 'Expected date (YYYY-MM-DD)',
4446
+ example: '2024-04-01'
4175
4447
  },
4176
- baseCurrency: {
4448
+ icon: {
4177
4449
  type: 'string',
4178
- description: 'Base currency code',
4179
- example: 'USD'
4450
+ description: 'Rule icon emoji',
4451
+ example: '🏠',
4452
+ nullable: true
4180
4453
  },
4181
- benchmark: {
4454
+ currency: {
4182
4455
  type: 'string',
4183
- description: 'Benchmark symbol',
4184
- example: 'SPY'
4185
- },
4186
- colorScheme: {
4456
+ description: 'Currency code',
4457
+ example: 'CNY'
4458
+ }
4459
+ },
4460
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4461
+ } as const;
4462
+
4463
+ export const $MonthlyForecastDto = {
4464
+ type: 'object',
4465
+ properties: {
4466
+ month: {
4187
4467
  type: 'string',
4188
- description: 'Color scheme',
4189
- enum: ['DARK', 'LIGHT']
4468
+ description: 'Month (YYYY-MM)',
4469
+ example: '2024-04'
4190
4470
  },
4191
- dateRange: {
4192
- type: 'string',
4193
- description: 'Date range filter',
4194
- example: '1y'
4471
+ expectedOutflow: {
4472
+ type: 'number',
4473
+ description: 'Total expected outflow for the month',
4474
+ example: 8500
4195
4475
  },
4196
- emergencyFund: {
4476
+ itemCount: {
4197
4477
  type: 'number',
4198
- description: 'Emergency fund amount',
4199
- example: 10000
4478
+ description: 'Number of expected transactions',
4479
+ example: 3
4200
4480
  },
4201
- 'filters.accounts': {
4202
- description: 'Account filter IDs',
4203
- type: 'array',
4204
- items: {
4205
- type: 'string'
4481
+ byCurrency: {
4482
+ type: 'object',
4483
+ description: 'Breakdown by currency',
4484
+ example: {
4485
+ CNY: 8500,
4486
+ USD: 100
4206
4487
  }
4207
4488
  },
4208
- 'filters.assetClasses': {
4209
- description: 'Asset class filters',
4489
+ items: {
4490
+ description: 'Individual forecast items',
4210
4491
  type: 'array',
4211
4492
  items: {
4212
- type: 'string'
4493
+ $ref: '#/components/schemas/ForecastItemDto'
4213
4494
  }
4214
- },
4215
- 'filters.dataSource': {
4216
- type: 'string',
4217
- description: 'Data source filter'
4218
- },
4219
- 'filters.symbol': {
4220
- type: 'string',
4221
- description: 'Symbol filter'
4222
- },
4223
- 'filters.tags': {
4224
- description: 'Tag filters',
4495
+ }
4496
+ },
4497
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4498
+ } as const;
4499
+
4500
+ export const $ForecastResponseDto = {
4501
+ type: 'object',
4502
+ properties: {
4503
+ forecast: {
4504
+ description: 'Monthly forecast data',
4225
4505
  type: 'array',
4226
4506
  items: {
4227
- type: 'string'
4507
+ $ref: '#/components/schemas/MonthlyForecastDto'
4228
4508
  }
4229
4509
  },
4230
- isExperimentalFeatures: {
4231
- type: 'boolean',
4232
- description: 'Enable experimental features'
4233
- },
4234
- isRestrictedView: {
4235
- type: 'boolean',
4236
- description: 'Enable restricted view mode'
4237
- },
4238
- language: {
4239
- type: 'string',
4240
- description: 'Language code',
4241
- example: 'en'
4242
- },
4243
- locale: {
4244
- type: 'string',
4245
- description: 'Locale code',
4246
- example: 'en-US'
4247
- },
4248
- projectedTotalAmount: {
4510
+ totalOutflow: {
4249
4511
  type: 'number',
4250
- description: 'Projected total amount',
4251
- example: 1000000
4512
+ description: 'Total expected outflow across all months',
4513
+ example: 25500
4252
4514
  },
4253
- retirementDate: {
4254
- type: 'string',
4255
- description: 'Retirement date in ISO 8601 format',
4256
- example: '2050-01-01'
4515
+ totalByCurrency: {
4516
+ type: 'object',
4517
+ description: 'Total by currency across all months',
4518
+ example: {
4519
+ CNY: 25500,
4520
+ USD: 300
4521
+ }
4257
4522
  },
4258
- savingsRate: {
4523
+ rulesCount: {
4259
4524
  type: 'number',
4260
- description: 'Savings rate percentage',
4261
- example: 0.2
4525
+ description: 'Number of active recurring rules included',
4526
+ example: 5
4262
4527
  },
4263
- viewMode: {
4528
+ periodStart: {
4264
4529
  type: 'string',
4265
- description: 'View mode',
4266
- enum: ['DEFAULT', 'ZEN']
4267
- }
4268
- }
4269
- } as const;
4270
-
4271
- export const $UpdatePropertyDto = {
4272
- type: 'object',
4273
- properties: {
4274
- value: {
4530
+ description: 'Forecast period start date',
4531
+ example: '2024-04-01'
4532
+ },
4533
+ periodEnd: {
4275
4534
  type: 'string',
4276
- description: 'Property value'
4535
+ description: 'Forecast period end date',
4536
+ example: '2024-06-30'
4277
4537
  }
4278
4538
  },
4279
- required: ['value']
4539
+ required: [
4540
+ 'forecast',
4541
+ 'totalOutflow',
4542
+ 'totalByCurrency',
4543
+ 'rulesCount',
4544
+ 'periodStart',
4545
+ 'periodEnd'
4546
+ ]
4280
4547
  } as const;
4281
4548
 
4282
4549
  export const $CreateTransactionRuleDto = {
@@ -4838,7 +5105,8 @@ export const $UpdateTransactionRuleDto = {
4838
5105
  },
4839
5106
  matchLogic: {
4840
5107
  type: 'string',
4841
- enum: ['OR', 'AND']
5108
+ enum: ['OR', 'AND'],
5109
+ default: 'OR'
4842
5110
  },
4843
5111
  amountMin: {
4844
5112
  type: 'number',
@@ -4852,12 +5120,9 @@ export const $UpdateTransactionRuleDto = {
4852
5120
  },
4853
5121
  priority: {
4854
5122
  type: 'number',
4855
- minimum: 0,
4856
- maximum: 1000
4857
- },
4858
- enabled: {
4859
- type: 'boolean',
4860
- description: 'Enable or disable the rule'
5123
+ default: 50,
5124
+ minimum: 0,
5125
+ maximum: 1000
4861
5126
  },
4862
5127
  additionalTags: {
4863
5128
  items: {
@@ -4868,6 +5133,10 @@ export const $UpdateTransactionRuleDto = {
4868
5133
  },
4869
5134
  additionalMetadata: {
4870
5135
  type: 'object'
5136
+ },
5137
+ enabled: {
5138
+ type: 'boolean',
5139
+ description: 'Enable or disable the rule'
4871
5140
  }
4872
5141
  }
4873
5142
  } as const;
@@ -4928,6 +5197,83 @@ export const $TestRuleResponseDto = {
4928
5197
  required: ['ruleId', 'matches', 'confidence', 'matchDetails']
4929
5198
  } as const;
4930
5199
 
5200
+ export const $CategoryCatalogEntryDto = {
5201
+ type: 'object',
5202
+ properties: {
5203
+ slug: {
5204
+ type: 'string',
5205
+ description: 'Category slug (single source-of-truth)',
5206
+ example: 'food'
5207
+ },
5208
+ scenario: {
5209
+ type: 'string',
5210
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5211
+ enum: [
5212
+ 'expense',
5213
+ 'income',
5214
+ 'investment',
5215
+ 'banking',
5216
+ 'transfer',
5217
+ 'payment'
5218
+ ],
5219
+ example: 'expense'
5220
+ },
5221
+ icon: {
5222
+ type: 'string',
5223
+ description: 'Lucide icon name',
5224
+ example: 'utensils'
5225
+ },
5226
+ regions: {
5227
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5228
+ example: ['*'],
5229
+ type: 'array',
5230
+ items: {
5231
+ type: 'string'
5232
+ }
5233
+ },
5234
+ categoryAccounts: {
5235
+ description:
5236
+ "Beancount account paths (categoryAccount) of the region-enabled system rules whose categoryKeywords include this slug (#816). System rules only (public endpoint — user rules excluded); one-to-many by design (e.g. 'utilities' → Electricity/Water/Internet/Gas), sorted, [] when no rule maps the slug.",
5237
+ example: [
5238
+ 'Expenses:Utilities:Electricity',
5239
+ 'Expenses:Utilities:Gas',
5240
+ 'Expenses:Utilities:Internet',
5241
+ 'Expenses:Utilities:Water'
5242
+ ],
5243
+ type: 'array',
5244
+ items: {
5245
+ type: 'string'
5246
+ }
5247
+ }
5248
+ },
5249
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
5250
+ } as const;
5251
+
5252
+ export const $CategoryCatalogListResponseDto = {
5253
+ type: 'object',
5254
+ properties: {
5255
+ items: {
5256
+ description: 'Category entries (region-scoped, query-filtered)',
5257
+ type: 'array',
5258
+ items: {
5259
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5260
+ }
5261
+ },
5262
+ total: {
5263
+ type: 'number',
5264
+ description:
5265
+ 'Total category entries for the region (before query filtering)',
5266
+ example: 30
5267
+ },
5268
+ region: {
5269
+ type: 'string',
5270
+ description: 'Region code',
5271
+ example: 'cn'
5272
+ }
5273
+ },
5274
+ required: ['items', 'total', 'region']
5275
+ } as const;
5276
+
4931
5277
  export const $CreateBeanEventDto = {
4932
5278
  type: 'object',
4933
5279
  properties: {
@@ -5091,6 +5437,14 @@ export const $OnboardingAccountDto = {
5091
5437
  description:
5092
5438
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5093
5439
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5440
+ },
5441
+ displayName: {
5442
+ type: 'string',
5443
+ description:
5444
+ 'User-set display name override (omit/null = keep the derived name)',
5445
+ nullable: true,
5446
+ maxLength: 50,
5447
+ example: 'Salary card'
5094
5448
  }
5095
5449
  },
5096
5450
  required: ['path', 'currency']
@@ -5670,7 +6024,8 @@ export const $UpdateMapperDefaultsDto = {
5670
6024
  type: 'string',
5671
6025
  description: 'Source account for transactions (Beancount format)',
5672
6026
  example: 'Assets:CN:Alipay:Balance',
5673
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6027
+ pattern:
6028
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5674
6029
  },
5675
6030
  currency: {
5676
6031
  type: 'string',
@@ -5684,13 +6039,15 @@ export const $UpdateMapperDefaultsDto = {
5684
6039
  type: 'string',
5685
6040
  description: 'Default expense account (optional)',
5686
6041
  example: 'Expenses:Unknown',
5687
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6042
+ pattern:
6043
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5688
6044
  },
5689
6045
  incomeAccount: {
5690
6046
  type: 'string',
5691
6047
  description: 'Default income account (optional)',
5692
6048
  example: 'Income:Unknown',
5693
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6049
+ pattern:
6050
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5694
6051
  },
5695
6052
  methodAccountMapping: {
5696
6053
  type: 'object',
@@ -5979,15 +6336,106 @@ export const $UncoveredFormatMissDto = {
5979
6336
  properties: {}
5980
6337
  } as const;
5981
6338
 
6339
+ export const $ClientParsedDataDto = {
6340
+ type: 'object',
6341
+ properties: {
6342
+ amount: {
6343
+ type: 'number',
6344
+ description: 'Transaction amount',
6345
+ example: 35
6346
+ },
6347
+ currency: {
6348
+ type: 'string',
6349
+ description: 'Currency code',
6350
+ example: 'CNY'
6351
+ },
6352
+ date: {
6353
+ type: 'string',
6354
+ description: 'Transaction date (ISO 8601)',
6355
+ example: '2026-08-15'
6356
+ },
6357
+ payee: {
6358
+ type: 'string',
6359
+ description: 'Payee/merchant name',
6360
+ example: 'Starbucks'
6361
+ },
6362
+ narration: {
6363
+ type: 'string',
6364
+ description: 'Transaction narration'
6365
+ },
6366
+ category: {
6367
+ type: 'string',
6368
+ description:
6369
+ 'Category in canonical form (catalog slug or Stage-2 rule keyword token, ADR-0116) — echo back verbatim from parsedData.category. Foreign forms (locale display names, account paths) are rejected with nlp.category.invalid.',
6370
+ example: 'food'
6371
+ },
6372
+ incomeType: {
6373
+ type: 'string',
6374
+ description: 'Income type',
6375
+ example: 'Salary'
6376
+ },
6377
+ incomeSource: {
6378
+ type: 'string',
6379
+ description: 'Income source',
6380
+ example: 'Anthropic Inc.'
6381
+ },
6382
+ symbol: {
6383
+ type: 'string',
6384
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6385
+ example: 'AAPL'
6386
+ },
6387
+ quantity: {
6388
+ type: 'number',
6389
+ description: 'Quantity of shares/units',
6390
+ example: 100
6391
+ },
6392
+ price: {
6393
+ type: 'number',
6394
+ description: 'Unit price per share/unit',
6395
+ example: 1900
6396
+ },
6397
+ investmentAction: {
6398
+ type: 'string',
6399
+ description: 'Investment action',
6400
+ enum: ['buy', 'sell'],
6401
+ example: 'buy'
6402
+ },
6403
+ paymentSource: {
6404
+ type: 'string',
6405
+ description: 'Payment source: asset (default) or liability (credit card)',
6406
+ enum: ['asset', 'liability'],
6407
+ example: 'asset'
6408
+ },
6409
+ liabilityHint: {
6410
+ type: 'string',
6411
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6412
+ example: 'CreditCard'
6413
+ },
6414
+ warning: {
6415
+ type: 'string',
6416
+ description:
6417
+ 'Display-only warning from the prior response; accepted but ignored.',
6418
+ example: 'Cross-currency settlement applies.'
6419
+ }
6420
+ }
6421
+ } as const;
6422
+
5982
6423
  export const $ProcessNlpDto = {
5983
6424
  type: 'object',
5984
6425
  properties: {
5985
6426
  message: {
5986
6427
  type: 'string',
5987
- description: 'Natural language text describing a transaction (Chinese)',
5988
- example: 'yesterday Starbucks spent 35 yuan',
6428
+ description:
6429
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6430
+ example: 'Starbucks 35',
5989
6431
  maxLength: 500
5990
6432
  },
6433
+ confirm: {
6434
+ type: 'boolean',
6435
+ description:
6436
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6437
+ example: true
6438
+ },
5991
6439
  sessionId: {
5992
6440
  type: 'string',
5993
6441
  description:
@@ -5995,14 +6443,18 @@ export const $ProcessNlpDto = {
5995
6443
  example: 'session_abc123'
5996
6444
  },
5997
6445
  parsedData: {
5998
- type: 'object',
5999
6446
  description:
6000
6447
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
6001
6448
  example: {
6002
6449
  amount: 35,
6003
6450
  currency: 'CNY',
6004
6451
  payee: 'Starbucks'
6005
- }
6452
+ },
6453
+ allOf: [
6454
+ {
6455
+ $ref: '#/components/schemas/ClientParsedDataDto'
6456
+ }
6457
+ ]
6006
6458
  },
6007
6459
  selectedRuleId: {
6008
6460
  type: 'string',
@@ -6013,11 +6465,29 @@ export const $ProcessNlpDto = {
6013
6465
  selectedAccount: {
6014
6466
  type: 'string',
6015
6467
  description:
6016
- 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i], or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6468
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i].path, or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6017
6469
  example: 'Expenses:Food:Coffee'
6470
+ },
6471
+ viewpointAccount: {
6472
+ type: 'string',
6473
+ description:
6474
+ 'Viewpoint account hint: the beancount path the user drilled into (e.g. from an account drill-down). Tie-break only — never overrides accounts resolved from the text. Must be an owned, OPEN Assets:/Liabilities: account; unresolvable hints are silently ignored.',
6475
+ example: 'Assets:CN:Bank:ICBC'
6476
+ },
6477
+ viewpointCategory: {
6478
+ type: 'string',
6479
+ description:
6480
+ "Viewpoint category hint: the ADR-0075 Group segment the user drilled into (e.g. 'Food'). Resolved to a concrete OPEN account in that group; tie-break only — never overrides a category resolved from the text.",
6481
+ example: 'Food'
6482
+ },
6483
+ viewpointFlow: {
6484
+ type: 'string',
6485
+ description:
6486
+ "Companion flow root for viewpointCategory ('income' | 'expense'), mirroring the ADR-0126 list-endpoint invariant. Derived from the session's routed intent (multi-turn) when absent; a first-turn flow-less category hint is dropped — send the flow explicitly.",
6487
+ enum: ['income', 'expense'],
6488
+ example: 'expense'
6018
6489
  }
6019
- },
6020
- required: ['message']
6490
+ }
6021
6491
  } as const;
6022
6492
 
6023
6493
  export const $NlpTransactionInfoDto = {
@@ -6083,7 +6553,9 @@ export const $NlpParsedDataDto = {
6083
6553
  },
6084
6554
  category: {
6085
6555
  type: 'string',
6086
- description: 'Category'
6556
+ description:
6557
+ 'Category in canonical form: a CATEGORY_CATALOG slug (GET /{region}/bean/categories) or a Stage-2 rule categoryKeywords token (ADR-0116 D2). Never a locale display name or a beancount account path. Echo back verbatim on confirm.',
6558
+ example: 'food'
6087
6559
  },
6088
6560
  incomeType: {
6089
6561
  type: 'string',
@@ -6329,6 +6801,24 @@ export const $NlpRuleConfirmationDataDto = {
6329
6801
  ]
6330
6802
  } as const;
6331
6803
 
6804
+ export const $NlpAccountCandidateDto = {
6805
+ type: 'object',
6806
+ properties: {
6807
+ path: {
6808
+ type: 'string',
6809
+ description: 'Canonical beancount account path (echo back on selection)',
6810
+ example: 'Expenses:Food:Dining'
6811
+ },
6812
+ name: {
6813
+ type: 'string',
6814
+ description:
6815
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6816
+ example: '餐饮'
6817
+ }
6818
+ },
6819
+ required: ['path', 'name']
6820
+ } as const;
6821
+
6332
6822
  export const $NlpAccountConfirmationDataDto = {
6333
6823
  type: 'object',
6334
6824
  properties: {
@@ -6344,10 +6834,11 @@ export const $NlpAccountConfirmationDataDto = {
6344
6834
  example: 'Expenses:Food:Drinks'
6345
6835
  },
6346
6836
  similarAccounts: {
6347
- description: 'Similar accounts for user selection',
6837
+ description:
6838
+ 'Similar accounts for user selection (path + localized name, #680)',
6348
6839
  type: 'array',
6349
6840
  items: {
6350
- type: 'string'
6841
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6351
6842
  }
6352
6843
  },
6353
6844
  errorMessage: {
@@ -6635,7 +7126,7 @@ export const $NlpResponseDto = {
6635
7126
  type: 'string',
6636
7127
  description:
6637
7128
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6638
- enum: ['transfer', 'banking', 'investment'],
7129
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6639
7130
  example: 'investment'
6640
7131
  },
6641
7132
  liabilitySubType: {
@@ -6838,7 +7329,7 @@ export const $PlatformListItemDto = {
6838
7329
  suggestedSegment: {
6839
7330
  type: 'string',
6840
7331
  description:
6841
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7332
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6842
7333
  },
6843
7334
  logoUrl: {
6844
7335
  type: 'string',
@@ -6851,6 +7342,13 @@ export const $PlatformListItemDto = {
6851
7342
  example: 'CN',
6852
7343
  nullable: true
6853
7344
  },
7345
+ category: {
7346
+ type: 'string',
7347
+ description:
7348
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7349
+ nullable: true,
7350
+ example: 'DigitalWallet'
7351
+ },
6854
7352
  isBound: {
6855
7353
  type: 'boolean',
6856
7354
  description: 'Whether user has accounts using this platform'
@@ -6865,6 +7363,7 @@ export const $PlatformListItemDto = {
6865
7363
  'suggestedSegment',
6866
7364
  'logoUrl',
6867
7365
  'countryCode',
7366
+ 'category',
6868
7367
  'isBound'
6869
7368
  ]
6870
7369
  } as const;
@@ -6900,7 +7399,7 @@ export const $PlatformMatchResultDto = {
6900
7399
  suggestedSegment: {
6901
7400
  type: 'string',
6902
7401
  description:
6903
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
7402
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6904
7403
  },
6905
7404
  logoUrl: {
6906
7405
  type: 'string',
@@ -6913,50 +7412,131 @@ export const $PlatformMatchResultDto = {
6913
7412
  example: 'CN',
6914
7413
  nullable: true
6915
7414
  },
6916
- matchType: {
7415
+ category: {
7416
+ type: 'string',
7417
+ description:
7418
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7419
+ nullable: true,
7420
+ example: 'DigitalWallet'
7421
+ },
7422
+ matchType: {
7423
+ type: 'string',
7424
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7425
+ enum: ['exact', 'prefix', 'substring']
7426
+ }
7427
+ },
7428
+ required: [
7429
+ 'id',
7430
+ 'name',
7431
+ 'canonical',
7432
+ 'type',
7433
+ 'suggestedSegment',
7434
+ 'logoUrl',
7435
+ 'countryCode',
7436
+ 'category',
7437
+ 'matchType'
7438
+ ]
7439
+ } as const;
7440
+
7441
+ export const $PlatformMatchResponseDto = {
7442
+ type: 'object',
7443
+ properties: {
7444
+ platforms: {
7445
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7446
+ type: 'array',
7447
+ items: {
7448
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7449
+ }
7450
+ },
7451
+ matchType: {
7452
+ type: 'string',
7453
+ description:
7454
+ "Overall match quality — top row's tier, or 'none' when no hits",
7455
+ enum: ['none', 'exact', 'prefix', 'substring']
7456
+ },
7457
+ total: {
7458
+ type: 'number',
7459
+ description: 'Total matches before LIMIT (truncation transparency)'
7460
+ },
7461
+ hasMore: {
7462
+ type: 'boolean',
7463
+ description: 'true when total > platforms.length (more matches exist)'
7464
+ }
7465
+ },
7466
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7467
+ } as const;
7468
+
7469
+ export const $PlatformStandardsPlatformDto = {
7470
+ type: 'object',
7471
+ properties: {
7472
+ id: {
7473
+ type: 'string',
7474
+ description: 'Global platform ID'
7475
+ },
7476
+ name: {
7477
+ type: 'string',
7478
+ description: 'Platform name (e.g., "ICBC")'
7479
+ },
7480
+ canonical: {
7481
+ type: 'string',
7482
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7483
+ },
7484
+ suggestedSegment: {
7485
+ type: 'string',
7486
+ description:
7487
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7488
+ },
7489
+ type: {
7490
+ type: 'string',
7491
+ description: 'Platform type',
7492
+ enum: [
7493
+ 'BANK',
7494
+ 'BROKERAGE',
7495
+ 'CRYPTO_EXCHANGE',
7496
+ 'PAYMENT',
7497
+ 'INVESTMENT',
7498
+ 'INSURANCE',
7499
+ 'OTHER'
7500
+ ]
7501
+ },
7502
+ category: {
6917
7503
  type: 'string',
6918
- description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6919
- enum: ['exact', 'prefix', 'substring']
7504
+ description:
7505
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7506
+ nullable: true,
7507
+ example: 'Bank'
6920
7508
  }
6921
7509
  },
6922
- required: [
6923
- 'id',
6924
- 'name',
6925
- 'canonical',
6926
- 'type',
6927
- 'suggestedSegment',
6928
- 'logoUrl',
6929
- 'countryCode',
6930
- 'matchType'
6931
- ]
7510
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
6932
7511
  } as const;
6933
7512
 
6934
- export const $PlatformMatchResponseDto = {
7513
+ export const $PlatformStandardsResponseDto = {
6935
7514
  type: 'object',
6936
7515
  properties: {
6937
- platforms: {
6938
- description: 'Ranked matches, best tier first (at most 10 rows)',
6939
- type: 'array',
6940
- items: {
6941
- $ref: '#/components/schemas/PlatformMatchResultDto'
6942
- }
7516
+ platform: {
7517
+ description: 'The selected platform (institution lock source)',
7518
+ allOf: [
7519
+ {
7520
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7521
+ }
7522
+ ]
6943
7523
  },
6944
- matchType: {
7524
+ region: {
6945
7525
  type: 'string',
6946
7526
  description:
6947
- "Overall match quality — top row's tier, or 'none' when no hits",
6948
- enum: ['none', 'exact', 'prefix', 'substring']
6949
- },
6950
- total: {
6951
- type: 'number',
6952
- description: 'Total matches before LIMIT (truncation transparency)'
7527
+ "Resolved template region (ISO 3166-1 alpha-2, UPPERCASE): the platform's own countryCode when set, else the region query param. For regions without a regional template file the template list falls back to the universal-only catalog while region still echoes the code.",
7528
+ example: 'CN'
6953
7529
  },
6954
- hasMore: {
6955
- type: 'boolean',
6956
- description: 'true when total > platforms.length (more matches exist)'
7530
+ templates: {
7531
+ description:
7532
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7533
+ type: 'array',
7534
+ items: {
7535
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7536
+ }
6957
7537
  }
6958
7538
  },
6959
- required: ['platforms', 'matchType', 'total', 'hasMore']
7539
+ required: ['platform', 'region', 'templates']
6960
7540
  } as const;
6961
7541
 
6962
7542
  export const $CreatePlatformDto = {
@@ -7060,7 +7640,8 @@ export const $UpdatePlatformDto = {
7060
7640
  },
7061
7641
  isActive: {
7062
7642
  type: 'boolean',
7063
- description: 'Whether the platform is active'
7643
+ description: 'Whether the platform is active',
7644
+ default: true
7064
7645
  }
7065
7646
  }
7066
7647
  } as const;
@@ -7223,7 +7804,8 @@ export const $AccountItemDto = {
7223
7804
  },
7224
7805
  displayName: {
7225
7806
  type: 'string',
7226
- description: 'Display name (last part of account path)',
7807
+ description:
7808
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7227
7809
  example: 'Savings'
7228
7810
  },
7229
7811
  balance: {
@@ -7259,7 +7841,8 @@ export const $PlatformGroupDto = {
7259
7841
  example: 'CMB Bank'
7260
7842
  },
7261
7843
  accounts: {
7262
- description: 'Accounts within this platform',
7844
+ description:
7845
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7263
7846
  type: 'array',
7264
7847
  items: {
7265
7848
  $ref: '#/components/schemas/AccountItemDto'
@@ -7267,7 +7850,8 @@ export const $PlatformGroupDto = {
7267
7850
  },
7268
7851
  totalBalance: {
7269
7852
  type: 'string',
7270
- description: 'FX-converted total balance in base currency',
7853
+ description:
7854
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7271
7855
  example: '100000.00'
7272
7856
  },
7273
7857
  balanceByCurrency: {
@@ -7286,7 +7870,7 @@ export const $PlatformGroupDto = {
7286
7870
  sharePct: {
7287
7871
  type: 'number',
7288
7872
  description:
7289
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7873
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7290
7874
  example: 42.5
7291
7875
  }
7292
7876
  },
@@ -7334,7 +7918,8 @@ export const $AccountsSummaryDto = {
7334
7918
  properties: {
7335
7919
  totalAccounts: {
7336
7920
  type: 'number',
7337
- description: 'Total number of accounts'
7921
+ description:
7922
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7338
7923
  },
7339
7924
  totalPlatforms: {
7340
7925
  type: 'number',
@@ -7392,7 +7977,8 @@ export const $AccountItemWithAssetClassDto = {
7392
7977
  },
7393
7978
  displayName: {
7394
7979
  type: 'string',
7395
- description: 'Display name (last part of account path)',
7980
+ description:
7981
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7396
7982
  example: 'Savings'
7397
7983
  },
7398
7984
  balance: {
@@ -7878,7 +8464,7 @@ export const $MonetaryDto = {
7878
8464
  example: 'USD'
7879
8465
  },
7880
8466
  baseCcyEquivalent: {
7881
- type: 'object',
8467
+ type: 'string',
7882
8468
  description: 'Converted to user base currency (Decimal string)',
7883
8469
  example: '21600',
7884
8470
  nullable: true
@@ -7954,13 +8540,13 @@ export const $HoldingPnlRowDto = {
7954
8540
  example: 'Assets:US:Broker:AAPL'
7955
8541
  },
7956
8542
  accountCcy: {
7957
- type: 'object',
8543
+ type: 'string',
7958
8544
  description: 'Account settlement currency (ISO 4217), from cost currency',
7959
8545
  nullable: true,
7960
8546
  example: 'USD'
7961
8547
  },
7962
8548
  brokerType: {
7963
- type: 'object',
8549
+ type: 'string',
7964
8550
  description: 'Broker type derived from Platform.type',
7965
8551
  nullable: true,
7966
8552
  example: 'broker'
@@ -7981,7 +8567,7 @@ export const $HoldingPnlRowDto = {
7981
8567
  example: 'EQUITY'
7982
8568
  },
7983
8569
  assetSubClass: {
7984
- type: 'object',
8570
+ type: 'string',
7985
8571
  nullable: true,
7986
8572
  example: 'STOCK'
7987
8573
  },
@@ -8028,14 +8614,14 @@ export const $HoldingPnlRowDto = {
8028
8614
  ]
8029
8615
  },
8030
8616
  unrealizedPnlBase: {
8031
- type: 'object',
8617
+ type: 'string',
8032
8618
  description:
8033
8619
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
8034
8620
  nullable: true,
8035
8621
  example: '6000'
8036
8622
  },
8037
8623
  unrealizedPnlPct: {
8038
- type: 'object',
8624
+ type: 'string',
8039
8625
  description: 'Unrealized P&L % (Decimal string)',
8040
8626
  nullable: true,
8041
8627
  example: '25'
@@ -8059,7 +8645,7 @@ export const $HoldingPnlRowDto = {
8059
8645
  ]
8060
8646
  },
8061
8647
  pctOfInvestedAssets: {
8062
- type: 'object',
8648
+ type: 'string',
8063
8649
  description:
8064
8650
  'Share of invested assets % (Decimal string); only for invested chartTokens',
8065
8651
  nullable: true,
@@ -8104,15 +8690,15 @@ export const $HoldingPnlWarningDto = {
8104
8690
  ]
8105
8691
  },
8106
8692
  symbol: {
8107
- type: 'object',
8693
+ type: 'string',
8108
8694
  nullable: true
8109
8695
  },
8110
8696
  accountId: {
8111
- type: 'object',
8697
+ type: 'string',
8112
8698
  nullable: true
8113
8699
  },
8114
8700
  currency: {
8115
- type: 'object',
8701
+ type: 'string',
8116
8702
  nullable: true
8117
8703
  }
8118
8704
  },
@@ -8176,6 +8762,216 @@ export const $AnonymousLoginResponseDto = {
8176
8762
  required: ['authToken']
8177
8763
  } as const;
8178
8764
 
8765
+ export const $ParserContributionMetaDto = {
8766
+ type: 'object',
8767
+ properties: {
8768
+ institution: {
8769
+ type: 'string',
8770
+ description: 'Institution slug (lowercase kebab-case)',
8771
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8772
+ example: 'icbc'
8773
+ },
8774
+ region: {
8775
+ type: 'string',
8776
+ enum: [
8777
+ 'cn',
8778
+ 'us',
8779
+ 'de',
8780
+ 'fr',
8781
+ 'gb',
8782
+ 'hk',
8783
+ 'jp',
8784
+ 'sg',
8785
+ 'au',
8786
+ 'ca',
8787
+ 'other'
8788
+ ]
8789
+ },
8790
+ accountType: {
8791
+ type: 'string',
8792
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8793
+ },
8794
+ format: {
8795
+ type: 'string',
8796
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8797
+ },
8798
+ institutionDisplayName: {
8799
+ type: 'string',
8800
+ example: '中国工商银行'
8801
+ },
8802
+ encoding: {
8803
+ type: 'string',
8804
+ example: 'utf-8'
8805
+ },
8806
+ delimiter: {
8807
+ type: 'string',
8808
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8809
+ },
8810
+ headerRows: {
8811
+ type: 'number',
8812
+ default: 1,
8813
+ description: 'Header row count; the client omits the field when it is 1'
8814
+ },
8815
+ notes: {
8816
+ type: 'string',
8817
+ maxLength: 2000
8818
+ }
8819
+ },
8820
+ required: ['institution', 'region', 'accountType', 'format']
8821
+ } as const;
8822
+
8823
+ export const $ParserContributionSamplesDto = {
8824
+ type: 'object',
8825
+ properties: {
8826
+ rows: {
8827
+ description:
8828
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8829
+ type: 'array',
8830
+ items: {
8831
+ type: 'object'
8832
+ }
8833
+ },
8834
+ rawHeaders: {
8835
+ type: 'array',
8836
+ items: {
8837
+ type: 'string'
8838
+ }
8839
+ }
8840
+ },
8841
+ required: ['rows']
8842
+ } as const;
8843
+
8844
+ export const $FieldHintDto = {
8845
+ type: 'object',
8846
+ properties: {
8847
+ columnName: {
8848
+ type: 'string',
8849
+ example: '交易日期'
8850
+ },
8851
+ format: {
8852
+ type: 'string',
8853
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8854
+ example: 'yyyy-MM-dd'
8855
+ },
8856
+ signConvention: {
8857
+ type: 'string',
8858
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8859
+ },
8860
+ creditColumn: {
8861
+ type: 'string'
8862
+ },
8863
+ debitColumn: {
8864
+ type: 'string'
8865
+ }
8866
+ },
8867
+ required: ['columnName']
8868
+ } as const;
8869
+
8870
+ export const $ParserContributionFieldHintsDto = {
8871
+ type: 'object',
8872
+ properties: {
8873
+ date: {
8874
+ $ref: '#/components/schemas/FieldHintDto'
8875
+ },
8876
+ amount: {
8877
+ $ref: '#/components/schemas/FieldHintDto'
8878
+ },
8879
+ description: {
8880
+ $ref: '#/components/schemas/FieldHintDto'
8881
+ },
8882
+ balance: {
8883
+ $ref: '#/components/schemas/FieldHintDto'
8884
+ },
8885
+ payee: {
8886
+ $ref: '#/components/schemas/FieldHintDto'
8887
+ },
8888
+ reference: {
8889
+ $ref: '#/components/schemas/FieldHintDto'
8890
+ },
8891
+ category: {
8892
+ $ref: '#/components/schemas/FieldHintDto'
8893
+ }
8894
+ },
8895
+ required: ['date', 'amount']
8896
+ } as const;
8897
+
8898
+ export const $ExpectedTransactionDto = {
8899
+ type: 'object',
8900
+ properties: {
8901
+ date: {
8902
+ type: 'string',
8903
+ example: '2026-08-01'
8904
+ },
8905
+ amount: {
8906
+ type: 'number',
8907
+ example: -45.5
8908
+ },
8909
+ description: {
8910
+ type: 'string',
8911
+ example: '星巴克-***店'
8912
+ },
8913
+ payee: {
8914
+ type: 'string'
8915
+ },
8916
+ category: {
8917
+ type: 'string'
8918
+ }
8919
+ },
8920
+ required: ['date', 'amount', 'description']
8921
+ } as const;
8922
+
8923
+ export const $ParserContributionExamplesDto = {
8924
+ type: 'object',
8925
+ properties: {
8926
+ expectedTransactions: {
8927
+ type: 'array',
8928
+ items: {
8929
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8930
+ }
8931
+ }
8932
+ },
8933
+ required: ['expectedTransactions']
8934
+ } as const;
8935
+
8936
+ export const $ParserContributionRequestDto = {
8937
+ type: 'object',
8938
+ properties: {
8939
+ meta: {
8940
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8941
+ },
8942
+ samples: {
8943
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8944
+ },
8945
+ fieldHints: {
8946
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8947
+ },
8948
+ examples: {
8949
+ description: 'Omitted entirely by the client when empty',
8950
+ allOf: [
8951
+ {
8952
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8953
+ }
8954
+ ]
8955
+ }
8956
+ },
8957
+ required: ['meta', 'samples', 'fieldHints']
8958
+ } as const;
8959
+
8960
+ export const $ParserContributionRelayResponseDto = {
8961
+ type: 'object',
8962
+ properties: {
8963
+ issueUrl: {
8964
+ type: 'string',
8965
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8966
+ },
8967
+ issueNumber: {
8968
+ type: 'number',
8969
+ example: 42
8970
+ }
8971
+ },
8972
+ required: ['issueUrl', 'issueNumber']
8973
+ } as const;
8974
+
8179
8975
  export const $SymbolSearchResultDto = {
8180
8976
  type: 'object',
8181
8977
  properties: {
@@ -8184,35 +8980,35 @@ export const $SymbolSearchResultDto = {
8184
8980
  example: 'AAPL'
8185
8981
  },
8186
8982
  name: {
8187
- type: 'object',
8983
+ type: 'string',
8188
8984
  example: 'Apple Inc.',
8189
8985
  nullable: true
8190
8986
  },
8191
8987
  exchange: {
8192
- type: 'object',
8988
+ type: 'string',
8193
8989
  example: 'US',
8194
8990
  nullable: true
8195
8991
  },
8196
8992
  assetType: {
8197
- type: 'object',
8993
+ type: 'string',
8198
8994
  description: 'OpenBB asset_type (e.g. stock, etf)',
8199
8995
  example: 'stock',
8200
8996
  nullable: true
8201
8997
  },
8202
8998
  assetClass: {
8203
- type: 'object',
8999
+ type: 'string',
8204
9000
  description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8205
9001
  example: 'EQUITY',
8206
9002
  nullable: true
8207
9003
  },
8208
9004
  assetSubClass: {
8209
- type: 'object',
9005
+ type: 'string',
8210
9006
  description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8211
9007
  example: 'STOCK',
8212
9008
  nullable: true
8213
9009
  },
8214
9010
  currency: {
8215
- type: 'object',
9011
+ type: 'string',
8216
9012
  description: 'Trading currency (extra_data or inferred from exchange)',
8217
9013
  example: 'USD',
8218
9014
  nullable: true
@@ -8229,90 +9025,90 @@ export const $SymbolQuoteDto = {
8229
9025
  example: 'AAPL'
8230
9026
  },
8231
9027
  name: {
8232
- type: 'object',
9028
+ type: 'string',
8233
9029
  example: 'Apple Inc.',
8234
9030
  nullable: true
8235
9031
  },
8236
9032
  exchange: {
8237
- type: 'object',
9033
+ type: 'string',
8238
9034
  example: 'US',
8239
9035
  nullable: true
8240
9036
  },
8241
9037
  assetType: {
8242
- type: 'object',
9038
+ type: 'string',
8243
9039
  description: 'OpenBB asset_type',
8244
9040
  example: 'stock',
8245
9041
  nullable: true
8246
9042
  },
8247
9043
  assetClass: {
8248
- type: 'object',
9044
+ type: 'string',
8249
9045
  description: 'IGN asset class',
8250
9046
  example: 'EQUITY',
8251
9047
  nullable: true
8252
9048
  },
8253
9049
  assetSubClass: {
8254
- type: 'object',
9050
+ type: 'string',
8255
9051
  description: 'IGN asset sub-class',
8256
9052
  example: 'STOCK',
8257
9053
  nullable: true
8258
9054
  },
8259
9055
  currency: {
8260
- type: 'object',
9056
+ type: 'string',
8261
9057
  description: 'Trading currency (extra_data or inferred from exchange)',
8262
9058
  example: 'USD',
8263
9059
  nullable: true
8264
9060
  },
8265
9061
  price: {
8266
- type: 'object',
9062
+ type: 'string',
8267
9063
  description: 'Latest price (Decimal string)',
8268
9064
  example: '189.84',
8269
9065
  nullable: true
8270
9066
  },
8271
9067
  priceDate: {
8272
- type: 'object',
9068
+ type: 'string',
8273
9069
  description: 'Date the price was observed (ISO yyyy-MM-dd)',
8274
9070
  example: '2026-08-05',
8275
9071
  nullable: true
8276
9072
  },
8277
9073
  changePercent: {
8278
- type: 'object',
9074
+ type: 'number',
8279
9075
  description:
8280
9076
  'Change vs previous close, in percentage points (1.7 == 1.7%). openbb stores change_percent as a normalized decimal; this exposes percentage points for frontend convenience.',
8281
9077
  example: 1.7,
8282
9078
  nullable: true
8283
9079
  },
8284
9080
  prevClose: {
8285
- type: 'object',
9081
+ type: 'string',
8286
9082
  description: 'Previous close (Decimal string)',
8287
9083
  nullable: true
8288
9084
  },
8289
9085
  open: {
8290
- type: 'object',
9086
+ type: 'string',
8291
9087
  description: 'Day open (Decimal string)',
8292
9088
  nullable: true
8293
9089
  },
8294
9090
  high: {
8295
- type: 'object',
9091
+ type: 'string',
8296
9092
  description: 'Day high (Decimal string)',
8297
9093
  nullable: true
8298
9094
  },
8299
9095
  low: {
8300
- type: 'object',
9096
+ type: 'string',
8301
9097
  description: 'Day low (Decimal string)',
8302
9098
  nullable: true
8303
9099
  },
8304
9100
  volume: {
8305
- type: 'object',
9101
+ type: 'string',
8306
9102
  description: 'Day volume (Decimal string)',
8307
9103
  nullable: true
8308
9104
  },
8309
9105
  yearHigh: {
8310
- type: 'object',
9106
+ type: 'string',
8311
9107
  description: '52-week high (Decimal string)',
8312
9108
  nullable: true
8313
9109
  },
8314
9110
  yearLow: {
8315
- type: 'object',
9111
+ type: 'string',
8316
9112
  description: '52-week low (Decimal string)',
8317
9113
  nullable: true
8318
9114
  }