@firela/api-types 0.0.0-canary.4cf7f99a → 0.0.0-canary.4d47c5f0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -124,6 +132,8 @@ export const $AccountResponseDto = {
124
132
  'STUDENT_LOAN',
125
133
  'CREDIT_CARD',
126
134
  'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
127
137
  'OTHER'
128
138
  ],
129
139
  nullable: true,
@@ -179,7 +189,8 @@ export const $AccountResponseDto = {
179
189
  },
180
190
  displayName: {
181
191
  type: 'string',
182
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
183
194
  example: 'Checking'
184
195
  },
185
196
  icon: {
@@ -195,7 +206,7 @@ export const $AccountResponseDto = {
195
206
  }
196
207
  },
197
208
  platformId: {
198
- type: 'object',
209
+ type: 'string',
199
210
  description: 'Platform ID (null if unbound)',
200
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
201
212
  },
@@ -275,6 +286,14 @@ export const $UpdateAccountDto = {
275
286
  description: 'Icon identifier',
276
287
  example: 'bank-custom'
277
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
278
297
  openDirectiveMeta: {
279
298
  type: 'object',
280
299
  description:
@@ -375,16 +394,43 @@ export const $AccountStandardResponseDto = {
375
394
  },
376
395
  name: {
377
396
  type: 'string',
378
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
379
399
  example: 'Housing Fund'
380
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
381
425
  description: {
382
426
  type: 'string',
383
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
384
429
  example: 'ICBC checking account for daily transactions'
385
430
  },
386
431
  tags: {
387
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
388
434
  example: ['bank', 'checking', 'primary'],
389
435
  type: 'array',
390
436
  items: {
@@ -395,9 +441,47 @@ export const $AccountStandardResponseDto = {
395
441
  type: 'string',
396
442
  description: 'Icon identifier for UI display',
397
443
  example: 'bank-icbc'
444
+ },
445
+ productCategory: {
446
+ type: 'string',
447
+ description:
448
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
449
+ enum: [
450
+ 'cash',
451
+ 'investment',
452
+ 'credit_card',
453
+ 'loan',
454
+ 'payable_tax',
455
+ 'other'
456
+ ],
457
+ example: 'investment'
458
+ },
459
+ assetClass: {
460
+ type: 'string',
461
+ description:
462
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
463
+ enum: [
464
+ 'LIQUIDITY',
465
+ 'EQUITY',
466
+ 'FIXED_INCOME',
467
+ 'PRECIOUS_METALS',
468
+ 'COMMODITY',
469
+ 'INSURANCE',
470
+ 'ALTERNATIVE_INVESTMENT',
471
+ 'PERSONAL_ASSETS',
472
+ 'LIABILITY',
473
+ 'REAL_ESTATE',
474
+ 'INDEX'
475
+ ]
476
+ },
477
+ assetSubClass: {
478
+ type: 'string',
479
+ description:
480
+ 'Asset sub-class (product type, derived at read time from classification rules)',
481
+ example: 'STOCK'
398
482
  }
399
483
  },
400
- required: ['path', 'type', 'description', 'tags', 'icon']
484
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
401
485
  } as const;
402
486
 
403
487
  export const $AccountStandardListResponseDto = {
@@ -458,7 +542,10 @@ export const $RegionConfigDto = {
458
542
  },
459
543
  locale: {
460
544
  type: 'string',
461
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
462
549
  }
463
550
  },
464
551
  required: ['currency', 'dateFormat', 'locale']
@@ -471,6 +558,12 @@ export const $RegionInfoDto = {
471
558
  type: 'string',
472
559
  example: 'de'
473
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
474
567
  displayName: {
475
568
  type: 'string',
476
569
  example: 'Germany'
@@ -489,7 +582,7 @@ export const $RegionInfoDto = {
489
582
  $ref: '#/components/schemas/RegionConfigDto'
490
583
  }
491
584
  },
492
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
493
586
  } as const;
494
587
 
495
588
  export const $RegionsMetadataResponseDto = {
@@ -736,7 +829,7 @@ export const $PostingResponseDto = {
736
829
  units: {
737
830
  type: 'string',
738
831
  description:
739
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
740
833
  example: '100.50'
741
834
  },
742
835
  currency: {
@@ -1102,7 +1195,7 @@ export const $PostingDetailDto = {
1102
1195
  units: {
1103
1196
  type: 'string',
1104
1197
  description:
1105
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1106
1199
  example: '100.50'
1107
1200
  },
1108
1201
  currency: {
@@ -1286,6 +1379,147 @@ export const $TransactionDetailDto = {
1286
1379
  ]
1287
1380
  } as const;
1288
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1289
1523
  export const $BalanceByCurrencyDto = {
1290
1524
  type: 'object',
1291
1525
  properties: {
@@ -1331,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1331
1565
  totalAmount: {
1332
1566
  type: 'string',
1333
1567
  description:
1334
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1568
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1335
1569
  example: '-6000.00'
1336
1570
  },
1337
1571
  currency: {
@@ -1357,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1357
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1358
1592
  } as const;
1359
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1360
1614
  export const $TransactionListResponseDto = {
1361
1615
  type: 'object',
1362
1616
  properties: {
@@ -1364,7 +1618,7 @@ export const $TransactionListResponseDto = {
1364
1618
  description: 'List of transactions',
1365
1619
  type: 'array',
1366
1620
  items: {
1367
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1368
1622
  }
1369
1623
  },
1370
1624
  total: {
@@ -1390,6 +1644,15 @@ export const $TransactionListResponseDto = {
1390
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1391
1645
  }
1392
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1393
1656
  }
1394
1657
  },
1395
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -2300,10 +2563,10 @@ export const $UpdatePayeeDto = {
2300
2563
  meta: {
2301
2564
  type: 'object',
2302
2565
  description:
2303
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2566
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2304
2567
  example: {
2305
2568
  location: 'Zhongguancun',
2306
- note: 'Updated note',
2569
+ note: 'Near subway station',
2307
2570
  favorite: true
2308
2571
  }
2309
2572
  },
@@ -2864,568 +3127,660 @@ export const $UpdateCommodityDto = {
2864
3127
  }
2865
3128
  } as const;
2866
3129
 
2867
- export const $CreateBeanPriceDto = {
3130
+ export const $CurrencyBalanceDto = {
2868
3131
  type: 'object',
2869
3132
  properties: {
2870
3133
  currency: {
2871
3134
  type: 'string',
2872
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2873
- example: 'USD'
2874
- },
2875
- quoteCurrency: {
2876
- type: 'string',
2877
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3135
+ description: 'ISO 4217 currency code',
2878
3136
  example: 'CNY'
2879
3137
  },
2880
- amount: {
2881
- type: 'number',
2882
- description:
2883
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2884
- example: 175.5,
2885
- minimum: 0
2886
- },
2887
- date: {
3138
+ balance: {
2888
3139
  type: 'string',
2889
- description: 'Price date (ISO 8601 format)',
2890
- example: '2024-11-05'
2891
- },
2892
- metadata: {
2893
- type: 'object',
2894
- description:
2895
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2896
- example: {
2897
- source: 'MANUAL',
2898
- note: 'Bank valuation report',
2899
- confidence: 0.95
2900
- }
3140
+ description: 'Balance amount',
3141
+ example: '500000.00'
2901
3142
  }
2902
3143
  },
2903
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3144
+ required: ['currency', 'balance']
2904
3145
  } as const;
2905
3146
 
2906
- export const $PriceResponseDto = {
3147
+ export const $TimeSeriesPointDto = {
2907
3148
  type: 'object',
2908
3149
  properties: {
2909
- id: {
3150
+ date: {
2910
3151
  type: 'string',
2911
- description: 'Unique identifier',
2912
- example: 'uuid-123-456'
3152
+ description: 'Date in YYYY-MM-DD format',
3153
+ example: '2024-06-15'
2913
3154
  },
2914
- userId: {
3155
+ value: {
2915
3156
  type: 'string',
2916
- description: 'User ID (owner of the price)',
2917
- example: 'user-123'
3157
+ description: 'Value at this date (in base currency)',
3158
+ example: '500000.00'
2918
3159
  },
2919
- currency: {
3160
+ change: {
2920
3161
  type: 'string',
2921
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2922
- example: 'BTC'
3162
+ description: 'Change from previous point',
3163
+ example: '5000.00'
2923
3164
  },
2924
- quoteCurrency: {
3165
+ assets: {
2925
3166
  type: 'string',
2926
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2927
- example: 'USD'
2928
- },
2929
- amount: {
2930
- type: 'number',
2931
- description:
2932
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2933
- example: 50000
3167
+ description: 'Total assets at this date (in base currency)',
3168
+ example: '494338.00'
2934
3169
  },
2935
- date: {
3170
+ liabilities: {
2936
3171
  type: 'string',
2937
- description:
2938
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2939
- example: '2024-01-01',
2940
- format: 'date'
3172
+ description: 'Total liabilities at this date (in base currency)',
3173
+ example: '310098.00'
2941
3174
  },
2942
- meta: {
2943
- type: 'object',
2944
- description:
2945
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2946
- example: {
2947
- source: 'MANUAL',
2948
- note: 'User-defined price',
2949
- confidence: 1
3175
+ byCurrency: {
3176
+ description: 'Multi-currency breakdown for this point',
3177
+ type: 'array',
3178
+ items: {
3179
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2950
3180
  }
3181
+ }
3182
+ },
3183
+ required: ['date', 'value']
3184
+ } as const;
3185
+
3186
+ export const $TrendSummaryDto = {
3187
+ type: 'object',
3188
+ properties: {
3189
+ startValue: {
3190
+ type: 'string',
3191
+ description: 'Value at start of period',
3192
+ example: '450000.00'
2951
3193
  },
2952
- createdAt: {
2953
- format: 'date-time',
3194
+ endValue: {
2954
3195
  type: 'string',
2955
- description: 'Creation timestamp',
2956
- example: '2024-11-03T10:00:00Z'
3196
+ description: 'Value at end of period',
3197
+ example: '500000.00'
2957
3198
  },
2958
- updatedAt: {
2959
- format: 'date-time',
3199
+ totalChange: {
2960
3200
  type: 'string',
2961
- description: 'Last update timestamp',
2962
- example: '2024-11-03T10:00:00Z'
3201
+ description: 'Total change over period',
3202
+ example: '50000.00'
3203
+ },
3204
+ totalChangePercentage: {
3205
+ type: 'string',
3206
+ description: 'Total change percentage',
3207
+ example: '+11.11%'
2963
3208
  }
2964
3209
  },
2965
- required: [
2966
- 'id',
2967
- 'userId',
2968
- 'currency',
2969
- 'quoteCurrency',
2970
- 'amount',
2971
- 'date',
2972
- 'meta',
2973
- 'createdAt',
2974
- 'updatedAt'
2975
- ]
3210
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2976
3211
  } as const;
2977
3212
 
2978
- export const $PriceListResponseDto = {
3213
+ export const $MultiCurrencyPointDto = {
2979
3214
  type: 'object',
2980
3215
  properties: {
2981
- items: {
2982
- description: 'List of prices',
3216
+ date: {
3217
+ type: 'string',
3218
+ description: 'Date in YYYY-MM-DD format',
3219
+ example: '2024-06-15'
3220
+ },
3221
+ byCurrency: {
3222
+ description: 'Balances by currency',
2983
3223
  type: 'array',
2984
3224
  items: {
2985
- $ref: '#/components/schemas/PriceResponseDto'
3225
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2986
3226
  }
2987
- },
2988
- total: {
2989
- type: 'number',
2990
- description: 'Total number of prices',
2991
- example: 42
2992
3227
  }
2993
3228
  },
2994
- required: ['items', 'total']
3229
+ required: ['date', 'byCurrency']
2995
3230
  } as const;
2996
3231
 
2997
- export const $UpdateBeanPriceDto = {
3232
+ export const $PortfolioTrendsResponseDto = {
2998
3233
  type: 'object',
2999
3234
  properties: {
3000
- currency: {
3001
- type: 'string',
3002
- description: 'Currency being priced'
3235
+ series: {
3236
+ description: 'Time series data points',
3237
+ type: 'array',
3238
+ items: {
3239
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3240
+ }
3003
3241
  },
3004
- quoteCurrency: {
3242
+ summary: {
3243
+ description: 'Period summary',
3244
+ allOf: [
3245
+ {
3246
+ $ref: '#/components/schemas/TrendSummaryDto'
3247
+ }
3248
+ ]
3249
+ },
3250
+ period: {
3005
3251
  type: 'string',
3006
- description: 'Quote currency (pricing currency)'
3252
+ description: 'Period requested',
3253
+ example: '6m'
3007
3254
  },
3008
- amount: {
3009
- type: 'number',
3010
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3011
- minimum: 0
3255
+ granularity: {
3256
+ type: 'string',
3257
+ description: 'Data granularity',
3258
+ example: 'month'
3012
3259
  },
3013
- date: {
3260
+ currency: {
3014
3261
  type: 'string',
3015
- description: 'Price date (ISO 8601 format)'
3262
+ description: 'Base currency for converted values',
3263
+ example: 'CNY'
3016
3264
  },
3017
- metadata: {
3018
- type: 'object',
3019
- description: 'Metadata'
3265
+ byCurrency: {
3266
+ description:
3267
+ 'Multi-currency time series (each point has currency breakdown)',
3268
+ type: 'array',
3269
+ items: {
3270
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3271
+ }
3272
+ },
3273
+ warnings: {
3274
+ description: 'Exchange rate warnings',
3275
+ type: 'array',
3276
+ items: {
3277
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3278
+ }
3020
3279
  }
3021
- }
3280
+ },
3281
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3022
3282
  } as const;
3023
3283
 
3024
- export const $CreateRecurringRuleDto = {
3284
+ export const $CashFlowPointDto = {
3025
3285
  type: 'object',
3026
3286
  properties: {
3027
- name: {
3287
+ month: {
3028
3288
  type: 'string',
3029
- description: 'Rule name (unique per user)',
3030
- maxLength: 100
3289
+ description: 'Month key (YYYY-MM)',
3290
+ example: '2024-03'
3031
3291
  },
3032
- icon: {
3292
+ income: {
3033
3293
  type: 'string',
3034
- description: 'Icon emoji',
3035
- maxLength: 10
3294
+ description: 'Income in base currency (absolute, converted)',
3295
+ example: '10000.00'
3036
3296
  },
3037
- frequency: {
3297
+ expense: {
3038
3298
  type: 'string',
3039
- description: 'Recurring frequency',
3040
- enum: [
3041
- 'WEEKLY',
3042
- 'BIWEEKLY',
3043
- 'MONTHLY',
3044
- 'BIMONTHLY',
3045
- 'QUARTERLY',
3046
- 'YEARLY',
3047
- 'CUSTOM'
3048
- ]
3049
- },
3050
- expectedAmount: {
3051
- type: 'number',
3052
- description: 'Expected amount (positive number)',
3053
- minimum: 0
3054
- },
3055
- expectedDay: {
3056
- type: 'number',
3057
- description: 'Expected day of month (1-31)',
3058
- minimum: 1,
3059
- maximum: 31
3060
- },
3061
- customIntervalDays: {
3062
- type: 'number',
3063
- description: 'Custom interval in days (required for CUSTOM frequency)',
3064
- minimum: 1
3299
+ description: 'Expense in base currency (absolute, converted)',
3300
+ example: '5000.00'
3065
3301
  },
3066
- currency: {
3302
+ netSavings: {
3067
3303
  type: 'string',
3068
- description: 'Currency code',
3069
- default: 'CNY',
3070
- maxLength: 10
3071
- },
3072
- matchPayeePattern: {
3304
+ description: 'netSavings = income − expense (savings positive)',
3305
+ example: '5000.00'
3306
+ }
3307
+ },
3308
+ required: ['month', 'income', 'expense', 'netSavings']
3309
+ } as const;
3310
+
3311
+ export const $CashFlowTrendSummaryDto = {
3312
+ type: 'object',
3313
+ properties: {
3314
+ totalIncome: {
3073
3315
  type: 'string',
3074
- description: 'Payee matching pattern (supports wildcards)',
3075
- maxLength: 200
3316
+ description: 'Total income across the period',
3317
+ example: '60000.00'
3076
3318
  },
3077
- matchAmountTolerance: {
3078
- type: 'number',
3079
- description: 'Amount tolerance percentage (0-1)',
3080
- default: 0.075,
3081
- minimum: 0,
3082
- maximum: 1
3319
+ totalExpense: {
3320
+ type: 'string',
3321
+ description: 'Total expense across the period',
3322
+ example: '30000.00'
3083
3323
  },
3084
- defaultExpenseAccount: {
3324
+ totalNetSavings: {
3085
3325
  type: 'string',
3086
- description: 'Default expense account for auto-create',
3087
- maxLength: 200
3326
+ description: 'income − expense across the period',
3327
+ example: '30000.00'
3088
3328
  },
3089
- defaultPaymentAccount: {
3329
+ averageMonthlyNetSavings: {
3090
3330
  type: 'string',
3091
- description: 'Default payment account for auto-create',
3092
- maxLength: 200
3093
- },
3094
- defaultPayee: {
3095
- type: 'string',
3096
- description: 'Default payee for auto-create',
3097
- maxLength: 200
3098
- },
3099
- autoCreate: {
3100
- type: 'boolean',
3101
- description: 'Auto-create transaction when expected date arrives',
3102
- default: false
3103
- },
3104
- startDate: {
3105
- type: 'string',
3106
- description: 'Rule start date (ISO format)'
3107
- },
3108
- endDate: {
3109
- type: 'string',
3110
- description: 'Rule end date (ISO format)'
3331
+ description:
3332
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3333
+ example: '5000.00'
3111
3334
  }
3112
3335
  },
3113
3336
  required: [
3114
- 'name',
3115
- 'frequency',
3116
- 'expectedAmount',
3117
- 'currency',
3118
- 'matchAmountTolerance',
3119
- 'autoCreate'
3337
+ 'totalIncome',
3338
+ 'totalExpense',
3339
+ 'totalNetSavings',
3340
+ 'averageMonthlyNetSavings'
3120
3341
  ]
3121
3342
  } as const;
3122
3343
 
3123
- export const $RecurringRuleResponseDto = {
3344
+ export const $CashFlowTrendsResponseDto = {
3124
3345
  type: 'object',
3125
3346
  properties: {
3126
- id: {
3127
- type: 'string',
3128
- description: 'Rule ID'
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
+ }
3129
3354
  },
3130
- userId: {
3131
- type: 'string',
3132
- description: 'User ID'
3355
+ summary: {
3356
+ description: 'Period totals',
3357
+ allOf: [
3358
+ {
3359
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3360
+ }
3361
+ ]
3133
3362
  },
3134
- name: {
3363
+ period: {
3135
3364
  type: 'string',
3136
- description: 'Rule name'
3137
- },
3138
- icon: {
3139
- type: 'object',
3140
- description: 'Icon emoji'
3365
+ description: 'Period requested',
3366
+ example: '6m'
3141
3367
  },
3142
- frequency: {
3368
+ granularity: {
3143
3369
  type: 'string',
3144
- description: 'Recurring frequency'
3145
- },
3146
- expectedAmount: {
3147
- type: 'number',
3148
- description: 'Expected amount'
3149
- },
3150
- expectedDay: {
3151
- type: 'object',
3152
- description: 'Expected day of month'
3370
+ description: 'Data granularity (v1 returns month buckets)',
3371
+ example: 'month'
3153
3372
  },
3154
- customIntervalDays: {
3155
- type: 'object',
3156
- description: 'Custom interval in days'
3373
+ currency: {
3374
+ type: 'string',
3375
+ description: 'Base currency for converted values',
3376
+ example: 'CNY'
3157
3377
  },
3378
+ warnings: {
3379
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3380
+ type: 'array',
3381
+ items: {
3382
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3383
+ }
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: {
3158
3412
  currency: {
3159
3413
  type: 'string',
3160
- description: 'Currency code'
3414
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3415
+ example: 'USD'
3161
3416
  },
3162
- matchPayeePattern: {
3163
- type: 'object',
3164
- description: 'Payee matching pattern'
3417
+ quoteCurrency: {
3418
+ type: 'string',
3419
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3420
+ example: 'CNY'
3165
3421
  },
3166
- matchAmountTolerance: {
3422
+ amount: {
3167
3423
  type: 'number',
3168
- 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
3169
3428
  },
3170
- defaultExpenseAccount: {
3171
- type: 'object',
3172
- description: 'Default expense account'
3429
+ date: {
3430
+ type: 'string',
3431
+ description: 'Price date (ISO 8601 format)',
3432
+ example: '2024-11-05'
3173
3433
  },
3174
- defaultPaymentAccount: {
3434
+ metadata: {
3175
3435
  type: 'object',
3176
- 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'
3177
3455
  },
3178
- defaultPayee: {
3179
- type: 'object',
3180
- description: 'Default payee'
3456
+ userId: {
3457
+ type: 'string',
3458
+ description: 'User ID (owner of the price)',
3459
+ example: 'user-123'
3181
3460
  },
3182
- isActive: {
3183
- type: 'boolean',
3184
- description: 'Whether rule is active'
3461
+ currency: {
3462
+ type: 'string',
3463
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3464
+ example: 'BTC'
3185
3465
  },
3186
- startDate: {
3466
+ quoteCurrency: {
3187
3467
  type: 'string',
3188
- description: 'Rule start date (YYYY-MM-DD)'
3468
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3469
+ example: 'USD'
3189
3470
  },
3190
- endDate: {
3191
- type: 'object',
3192
- 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
3193
3476
  },
3194
- autoCreate: {
3195
- type: 'boolean',
3196
- 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'
3197
3483
  },
3198
- lastOccurrence: {
3484
+ meta: {
3199
3485
  type: 'object',
3200
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3201
- },
3202
- totalCount: {
3203
- type: 'number',
3204
- 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
+ }
3205
3493
  },
3206
3494
  createdAt: {
3207
3495
  format: 'date-time',
3208
3496
  type: 'string',
3209
- description: 'Created at timestamp'
3497
+ description: 'Creation timestamp',
3498
+ example: '2024-11-03T10:00:00Z'
3210
3499
  },
3211
3500
  updatedAt: {
3212
3501
  format: 'date-time',
3213
3502
  type: 'string',
3214
- description: 'Updated at timestamp'
3503
+ description: 'Last update timestamp',
3504
+ example: '2024-11-03T10:00:00Z'
3215
3505
  }
3216
3506
  },
3217
3507
  required: [
3218
3508
  'id',
3219
3509
  'userId',
3220
- 'name',
3221
- 'frequency',
3222
- 'expectedAmount',
3223
3510
  'currency',
3224
- 'matchAmountTolerance',
3225
- 'isActive',
3226
- 'startDate',
3227
- 'autoCreate',
3228
- 'totalCount',
3511
+ 'quoteCurrency',
3512
+ 'amount',
3513
+ 'date',
3514
+ 'meta',
3229
3515
  'createdAt',
3230
3516
  'updatedAt'
3231
3517
  ]
3232
3518
  } as const;
3233
3519
 
3234
- export const $CreateRuleFromTransactionDto = {
3520
+ export const $PriceListResponseDto = {
3235
3521
  type: 'object',
3236
3522
  properties: {
3237
- frequency: {
3238
- type: 'string',
3239
- description: 'Recurring frequency',
3240
- enum: [
3241
- 'WEEKLY',
3242
- 'BIWEEKLY',
3243
- 'MONTHLY',
3244
- 'BIMONTHLY',
3245
- 'QUARTERLY',
3246
- 'YEARLY',
3247
- 'CUSTOM'
3248
- ],
3249
- example: 'MONTHLY'
3523
+ items: {
3524
+ description: 'List of prices',
3525
+ type: 'array',
3526
+ items: {
3527
+ $ref: '#/components/schemas/PriceResponseDto'
3528
+ }
3250
3529
  },
3251
- name: {
3252
- type: 'string',
3253
- description: 'Optional name override (default: transaction payee)',
3254
- maxLength: 100
3530
+ total: {
3531
+ type: 'number',
3532
+ description: 'Total number of prices',
3533
+ example: 42
3534
+ }
3535
+ },
3536
+ required: ['items', 'total']
3537
+ } as const;
3538
+
3539
+ export const $UpdateBeanPriceDto = {
3540
+ type: 'object',
3541
+ properties: {
3542
+ currency: {
3543
+ type: 'string',
3544
+ description: 'Currency being priced'
3255
3545
  },
3256
- icon: {
3546
+ quoteCurrency: {
3257
3547
  type: 'string',
3258
- description: 'Optional icon emoji',
3259
- maxLength: 10
3548
+ description: 'Quote currency (pricing currency)'
3549
+ },
3550
+ amount: {
3551
+ type: 'number',
3552
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3553
+ minimum: 0
3554
+ },
3555
+ date: {
3556
+ type: 'string',
3557
+ description: 'Price date (ISO 8601 format)'
3558
+ },
3559
+ metadata: {
3560
+ type: 'object',
3561
+ description: 'Metadata'
3562
+ }
3563
+ }
3564
+ } as const;
3565
+
3566
+ export const $DeleteOwnUserDto = {
3567
+ type: 'object',
3568
+ properties: {
3569
+ accessToken: {
3570
+ type: 'string',
3571
+ description: 'Access token for user verification',
3572
+ example: 'abc123xyz'
3260
3573
  }
3261
3574
  },
3262
- required: ['frequency']
3575
+ required: ['accessToken']
3263
3576
  } as const;
3264
3577
 
3265
- export const $RecurringRuleWithStatsResponseDto = {
3578
+ export const $UserSettingsResponseDto = {
3579
+ type: 'object',
3580
+ properties: {
3581
+ baseCurrency: {
3582
+ type: 'string',
3583
+ description:
3584
+ 'Stored base currency choice (ISO 4217) for net-worth/report aggregation. null = user never chose; aggregates fall back to the region default at display time (#713).',
3585
+ example: 'USD',
3586
+ nullable: true
3587
+ }
3588
+ },
3589
+ required: ['baseCurrency']
3590
+ } as const;
3591
+
3592
+ export const $UserResponseDto = {
3266
3593
  type: 'object',
3267
3594
  properties: {
3268
3595
  id: {
3269
3596
  type: 'string',
3270
- description: 'Rule ID'
3597
+ description: 'User ID'
3271
3598
  },
3272
- userId: {
3599
+ role: {
3273
3600
  type: 'string',
3274
- description: 'User ID'
3601
+ description: 'Assigned user role'
3275
3602
  },
3276
- name: {
3603
+ permissions: {
3604
+ description: 'Permission strings',
3605
+ type: 'array',
3606
+ items: {
3607
+ type: 'string'
3608
+ }
3609
+ },
3610
+ settings: {
3611
+ description: 'User settings',
3612
+ allOf: [
3613
+ {
3614
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3615
+ }
3616
+ ]
3617
+ }
3618
+ },
3619
+ required: ['id', 'role', 'permissions', 'settings']
3620
+ } as const;
3621
+
3622
+ export const $SignupDto = {
3623
+ type: 'object',
3624
+ properties: {
3625
+ turnstileToken: {
3277
3626
  type: 'string',
3278
- description: 'Rule name'
3627
+ description:
3628
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3629
+ example: '0.abc123def456...'
3630
+ }
3631
+ }
3632
+ } as const;
3633
+
3634
+ export const $SignupResponseDto = {
3635
+ type: 'object',
3636
+ properties: {
3637
+ authToken: {
3638
+ type: 'string',
3639
+ description: 'JWT auth token',
3640
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3279
3641
  },
3280
- icon: {
3281
- type: 'object',
3282
- description: 'Icon emoji'
3642
+ accessToken: {
3643
+ type: 'string',
3644
+ description: 'Auto-generated access token'
3283
3645
  },
3284
- frequency: {
3646
+ role: {
3285
3647
  type: 'string',
3286
- description: 'Recurring frequency'
3648
+ description: 'Assigned user role',
3649
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3650
+ }
3651
+ },
3652
+ required: ['authToken', 'accessToken', 'role']
3653
+ } as const;
3654
+
3655
+ export const $UpdateUserSettingDto = {
3656
+ type: 'object',
3657
+ properties: {
3658
+ secId: {
3659
+ type: 'number',
3660
+ description: 'Security ID'
3287
3661
  },
3288
- expectedAmount: {
3662
+ annualInterestRate: {
3289
3663
  type: 'number',
3290
- description: 'Expected amount'
3664
+ description: 'Annual interest rate',
3665
+ example: 0.05
3291
3666
  },
3292
- expectedDay: {
3293
- type: 'object',
3294
- description: 'Expected day of month'
3667
+ currency: {
3668
+ type: 'string',
3669
+ description: 'Currency code',
3670
+ example: 'USD'
3295
3671
  },
3296
- customIntervalDays: {
3297
- type: 'object',
3298
- description: 'Custom interval in days'
3672
+ baseCurrency: {
3673
+ type: 'string',
3674
+ description: 'Base currency code',
3675
+ example: 'USD'
3299
3676
  },
3300
- currency: {
3677
+ benchmark: {
3301
3678
  type: 'string',
3302
- description: 'Currency code'
3679
+ description: 'Benchmark symbol',
3680
+ example: 'SPY'
3303
3681
  },
3304
- matchPayeePattern: {
3305
- type: 'object',
3306
- description: 'Payee matching pattern'
3682
+ colorScheme: {
3683
+ type: 'string',
3684
+ description: 'Color scheme',
3685
+ enum: ['DARK', 'LIGHT']
3307
3686
  },
3308
- matchAmountTolerance: {
3309
- type: 'number',
3310
- description: 'Amount tolerance percentage'
3687
+ dateRange: {
3688
+ type: 'string',
3689
+ description: 'Date range filter',
3690
+ example: '1y'
3311
3691
  },
3312
- defaultExpenseAccount: {
3313
- type: 'object',
3314
- description: 'Default expense account'
3692
+ emergencyFund: {
3693
+ type: 'number',
3694
+ description: 'Emergency fund amount',
3695
+ example: 10000
3315
3696
  },
3316
- defaultPaymentAccount: {
3317
- type: 'object',
3318
- description: 'Default payment account'
3697
+ 'filters.accounts': {
3698
+ description: 'Account filter IDs',
3699
+ type: 'array',
3700
+ items: {
3701
+ type: 'string'
3702
+ }
3319
3703
  },
3320
- defaultPayee: {
3321
- type: 'object',
3322
- description: 'Default payee'
3704
+ 'filters.assetClasses': {
3705
+ description: 'Asset class filters',
3706
+ type: 'array',
3707
+ items: {
3708
+ type: 'string'
3709
+ }
3323
3710
  },
3324
- isActive: {
3325
- type: 'boolean',
3326
- description: 'Whether rule is active'
3711
+ 'filters.dataSource': {
3712
+ type: 'string',
3713
+ description: 'Data source filter'
3327
3714
  },
3328
- startDate: {
3715
+ 'filters.symbol': {
3329
3716
  type: 'string',
3330
- description: 'Rule start date (YYYY-MM-DD)'
3717
+ description: 'Symbol filter'
3331
3718
  },
3332
- endDate: {
3333
- type: 'object',
3334
- description: 'Rule end date (YYYY-MM-DD)'
3719
+ 'filters.tags': {
3720
+ description: 'Tag filters',
3721
+ type: 'array',
3722
+ items: {
3723
+ type: 'string'
3724
+ }
3335
3725
  },
3336
- autoCreate: {
3726
+ isExperimentalFeatures: {
3337
3727
  type: 'boolean',
3338
- description: 'Auto-create transaction on expected date'
3339
- },
3340
- lastOccurrence: {
3341
- type: 'object',
3342
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3728
+ description: 'Enable experimental features'
3343
3729
  },
3344
- totalCount: {
3345
- type: 'number',
3346
- description: 'Total matched transactions count'
3730
+ isRestrictedView: {
3731
+ type: 'boolean',
3732
+ description: 'Enable restricted view mode'
3347
3733
  },
3348
- createdAt: {
3349
- format: 'date-time',
3734
+ language: {
3350
3735
  type: 'string',
3351
- description: 'Created at timestamp'
3736
+ description: 'Language code',
3737
+ example: 'en'
3352
3738
  },
3353
- updatedAt: {
3354
- format: 'date-time',
3739
+ locale: {
3355
3740
  type: 'string',
3356
- description: 'Updated at timestamp'
3357
- },
3358
- pendingCount: {
3359
- type: 'number',
3360
- description: 'Number of pending expected transactions'
3361
- },
3362
- overdueCount: {
3363
- type: 'number',
3364
- description: 'Number of overdue expected transactions'
3365
- },
3366
- nextExpectedDate: {
3367
- type: 'object',
3368
- description: 'Next expected date (YYYY-MM-DD)'
3741
+ description: 'Locale code',
3742
+ example: 'en-US'
3369
3743
  },
3370
- totalAmount: {
3744
+ projectedTotalAmount: {
3371
3745
  type: 'number',
3372
- description: 'Total amount of all matched transactions'
3746
+ description: 'Projected total amount',
3747
+ example: 1000000
3373
3748
  },
3374
- averageAmount: {
3375
- type: 'number',
3376
- description: 'Average amount per transaction'
3749
+ retirementDate: {
3750
+ type: 'string',
3751
+ description: 'Retirement date in ISO 8601 format',
3752
+ example: '2050-01-01'
3377
3753
  },
3378
- transactionCount: {
3754
+ savingsRate: {
3379
3755
  type: 'number',
3380
- description: 'Number of matched transactions'
3756
+ description: 'Savings rate percentage',
3757
+ example: 0.2
3381
3758
  },
3382
- firstDate: {
3383
- type: 'object',
3384
- description: 'First matched transaction date (YYYY-MM-DD)'
3385
- },
3386
- lastDate: {
3387
- type: 'object',
3388
- description: 'Last matched transaction date (YYYY-MM-DD)'
3389
- },
3390
- variance: {
3391
- type: 'number',
3392
- description: 'Amount variance (standard deviation squared)'
3393
- },
3394
- upcomingCount: {
3395
- type: 'number',
3396
- description: 'Number of upcoming expected transactions'
3759
+ viewMode: {
3760
+ type: 'string',
3761
+ description: 'View mode',
3762
+ enum: ['DEFAULT', 'ZEN']
3763
+ }
3764
+ }
3765
+ } as const;
3766
+
3767
+ export const $UpdatePropertyDto = {
3768
+ type: 'object',
3769
+ properties: {
3770
+ value: {
3771
+ type: 'string',
3772
+ description: 'Property value'
3397
3773
  }
3398
3774
  },
3399
- required: [
3400
- 'id',
3401
- 'userId',
3402
- 'name',
3403
- 'frequency',
3404
- 'expectedAmount',
3405
- 'currency',
3406
- 'matchAmountTolerance',
3407
- 'isActive',
3408
- 'startDate',
3409
- 'autoCreate',
3410
- 'totalCount',
3411
- 'createdAt',
3412
- 'updatedAt',
3413
- 'pendingCount',
3414
- 'overdueCount',
3415
- 'totalAmount',
3416
- 'averageAmount',
3417
- 'transactionCount',
3418
- 'variance',
3419
- 'upcomingCount'
3420
- ]
3775
+ required: ['value']
3421
3776
  } as const;
3422
3777
 
3423
- export const $UpdateRecurringRuleDto = {
3778
+ export const $CreateRecurringRuleDto = {
3424
3779
  type: 'object',
3425
3780
  properties: {
3426
3781
  name: {
3427
3782
  type: 'string',
3428
- description: 'Rule name',
3783
+ description: 'Rule name (unique per user)',
3429
3784
  maxLength: 100
3430
3785
  },
3431
3786
  icon: {
@@ -3448,7 +3803,7 @@ export const $UpdateRecurringRuleDto = {
3448
3803
  },
3449
3804
  expectedAmount: {
3450
3805
  type: 'number',
3451
- description: 'Expected amount',
3806
+ description: 'Expected amount (positive number)',
3452
3807
  minimum: 0
3453
3808
  },
3454
3809
  expectedDay: {
@@ -3459,7 +3814,7 @@ export const $UpdateRecurringRuleDto = {
3459
3814
  },
3460
3815
  customIntervalDays: {
3461
3816
  type: 'number',
3462
- description: 'Custom interval in days',
3817
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3463
3818
  minimum: 1
3464
3819
  },
3465
3820
  currency: {
@@ -3469,118 +3824,136 @@ export const $UpdateRecurringRuleDto = {
3469
3824
  },
3470
3825
  matchPayeePattern: {
3471
3826
  type: 'string',
3472
- description: 'Payee matching pattern',
3827
+ description: 'Payee matching pattern (supports wildcards)',
3473
3828
  maxLength: 200
3474
3829
  },
3475
3830
  matchAmountTolerance: {
3476
3831
  type: 'number',
3477
3832
  description: 'Amount tolerance percentage (0-1)',
3833
+ default: 0.075,
3478
3834
  minimum: 0,
3479
3835
  maximum: 1
3480
3836
  },
3481
3837
  defaultExpenseAccount: {
3482
3838
  type: 'string',
3483
- description: 'Default expense account',
3839
+ description: 'Default expense account for auto-create',
3484
3840
  maxLength: 200
3485
3841
  },
3486
3842
  defaultPaymentAccount: {
3487
3843
  type: 'string',
3488
- description: 'Default payment account',
3844
+ description: 'Default payment account for auto-create',
3489
3845
  maxLength: 200
3490
3846
  },
3491
3847
  defaultPayee: {
3492
3848
  type: 'string',
3493
- description: 'Default payee',
3849
+ description: 'Default payee for auto-create',
3494
3850
  maxLength: 200
3495
3851
  },
3496
3852
  autoCreate: {
3497
3853
  type: 'boolean',
3498
- description: 'Auto-create transaction'
3854
+ description: 'Auto-create transaction when expected date arrives',
3855
+ default: false
3499
3856
  },
3500
- isActive: {
3501
- type: 'boolean',
3502
- description: 'Rule active status'
3857
+ startDate: {
3858
+ type: 'string',
3859
+ description: 'Rule start date (ISO format)'
3503
3860
  },
3504
3861
  endDate: {
3505
3862
  type: 'string',
3506
3863
  description: 'Rule end date (ISO format)'
3507
3864
  }
3508
- }
3865
+ },
3866
+ required: [
3867
+ 'name',
3868
+ 'frequency',
3869
+ 'expectedAmount',
3870
+ 'matchAmountTolerance',
3871
+ 'autoCreate'
3872
+ ]
3509
3873
  } as const;
3510
3874
 
3511
- export const $ExpectedTransactionRuleDto = {
3875
+ export const $RecurringRuleResponseDto = {
3512
3876
  type: 'object',
3513
3877
  properties: {
3878
+ id: {
3879
+ type: 'string',
3880
+ description: 'Rule ID'
3881
+ },
3882
+ userId: {
3883
+ type: 'string',
3884
+ description: 'User ID'
3885
+ },
3514
3886
  name: {
3515
3887
  type: 'string',
3516
3888
  description: 'Rule name'
3517
3889
  },
3518
3890
  icon: {
3519
- type: 'object',
3520
- description: 'Rule icon'
3891
+ type: 'string',
3892
+ description: 'Icon emoji'
3521
3893
  },
3522
3894
  frequency: {
3523
3895
  type: 'string',
3524
- 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'
3525
3909
  },
3526
3910
  currency: {
3527
3911
  type: 'string',
3528
3912
  description: 'Currency code'
3529
- }
3530
- },
3531
- required: ['name', 'frequency', 'currency']
3532
- } as const;
3533
-
3534
- export const $ExpectedTransactionResponseDto = {
3535
- type: 'object',
3536
- properties: {
3537
- id: {
3538
- type: 'string',
3539
- description: 'Expected transaction ID'
3540
3913
  },
3541
- userId: {
3914
+ matchPayeePattern: {
3542
3915
  type: 'string',
3543
- description: 'User ID'
3916
+ description: 'Payee matching pattern'
3544
3917
  },
3545
- ruleId: {
3546
- type: 'string',
3547
- description: 'Associated rule ID'
3918
+ matchAmountTolerance: {
3919
+ type: 'number',
3920
+ description: 'Amount tolerance percentage'
3548
3921
  },
3549
- expectedDate: {
3922
+ defaultExpenseAccount: {
3550
3923
  type: 'string',
3551
- description: 'Expected date (YYYY-MM-DD)'
3924
+ description: 'Default expense account'
3552
3925
  },
3553
- expectedAmount: {
3554
- type: 'number',
3555
- description: 'Expected amount'
3926
+ defaultPaymentAccount: {
3927
+ type: 'string',
3928
+ description: 'Default payment account'
3556
3929
  },
3557
- status: {
3930
+ defaultPayee: {
3558
3931
  type: 'string',
3559
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3932
+ description: 'Default payee'
3560
3933
  },
3561
- matchedTransactionId: {
3562
- type: 'object',
3563
- description: 'Matched transaction ID'
3934
+ isActive: {
3935
+ type: 'boolean',
3936
+ description: 'Whether rule is active'
3564
3937
  },
3565
- matchedAt: {
3566
- type: 'object',
3567
- description: 'Match timestamp (ISO 8601)'
3938
+ startDate: {
3939
+ type: 'string',
3940
+ description: 'Rule start date (YYYY-MM-DD)'
3568
3941
  },
3569
- matchConfidence: {
3570
- type: 'object',
3571
- description: 'Match confidence score (0-1)'
3942
+ endDate: {
3943
+ type: 'string',
3944
+ description: 'Rule end date (YYYY-MM-DD)'
3572
3945
  },
3573
- isOverdue: {
3946
+ autoCreate: {
3574
3947
  type: 'boolean',
3575
- description: 'Whether this expected transaction is overdue'
3948
+ description: 'Auto-create transaction on expected date'
3576
3949
  },
3577
- rule: {
3578
- description: 'Rule information',
3579
- allOf: [
3580
- {
3581
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3582
- }
3583
- ]
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'
3584
3957
  },
3585
3958
  createdAt: {
3586
3959
  format: 'date-time',
@@ -3596,647 +3969,581 @@ export const $ExpectedTransactionResponseDto = {
3596
3969
  required: [
3597
3970
  'id',
3598
3971
  'userId',
3599
- 'ruleId',
3600
- 'expectedDate',
3972
+ 'name',
3973
+ 'frequency',
3601
3974
  'expectedAmount',
3602
- 'status',
3603
- 'isOverdue',
3604
- 'rule',
3975
+ 'currency',
3976
+ 'matchAmountTolerance',
3977
+ 'isActive',
3978
+ 'startDate',
3979
+ 'autoCreate',
3980
+ 'totalCount',
3605
3981
  'createdAt',
3606
3982
  'updatedAt'
3607
3983
  ]
3608
3984
  } as const;
3609
3985
 
3610
- export const $ExpectedTransactionListResponseDto = {
3986
+ export const $CreateRuleFromTransactionDto = {
3611
3987
  type: 'object',
3612
3988
  properties: {
3613
- items: {
3614
- type: 'array',
3615
- items: {
3616
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3617
- }
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'
3618
4002
  },
3619
- total: {
3620
- type: 'number',
3621
- description: 'Total count'
3622
- }
3623
- },
3624
- required: ['items', 'total']
3625
- } as const;
3626
-
3627
- export const $ConfirmMatchDto = {
3628
- type: 'object',
3629
- properties: {
3630
- transactionId: {
4003
+ name: {
3631
4004
  type: 'string',
3632
- 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
3633
4012
  }
3634
4013
  },
3635
- required: ['transactionId']
4014
+ required: ['frequency']
3636
4015
  } as const;
3637
4016
 
3638
- export const $EnterNowDto = {
4017
+ export const $RecurringRuleWithStatsResponseDto = {
3639
4018
  type: 'object',
3640
4019
  properties: {
3641
- expenseAccount: {
4020
+ id: {
3642
4021
  type: 'string',
3643
- description:
3644
- 'Override expense account (uses rule default if not provided)',
3645
- maxLength: 200
4022
+ description: 'Rule ID'
3646
4023
  },
3647
- paymentAccount: {
4024
+ userId: {
3648
4025
  type: 'string',
3649
- description:
3650
- 'Override payment account (uses rule default if not provided)',
3651
- maxLength: 200
4026
+ description: 'User ID'
3652
4027
  },
3653
- amount: {
3654
- type: 'number',
3655
- description: 'Override amount (uses expected amount if not provided)',
3656
- minimum: 0
3657
- },
3658
- payee: {
4028
+ name: {
3659
4029
  type: 'string',
3660
- description: 'Override payee (uses rule default if not provided)',
3661
- maxLength: 200
4030
+ description: 'Rule name'
3662
4031
  },
3663
- narration: {
3664
- type: 'string',
3665
- description: 'Optional narration',
3666
- maxLength: 500
3667
- }
3668
- }
3669
- } as const;
3670
-
3671
- export const $ForecastItemDto = {
3672
- type: 'object',
3673
- properties: {
3674
- rule: {
4032
+ icon: {
3675
4033
  type: 'string',
3676
- description: 'Rule name',
3677
- example: 'Rent'
4034
+ description: 'Icon emoji'
3678
4035
  },
3679
- ruleId: {
4036
+ frequency: {
3680
4037
  type: 'string',
3681
- description: 'Rule ID',
3682
- example: 'clx123...'
4038
+ description: 'Recurring frequency'
3683
4039
  },
3684
- amount: {
4040
+ expectedAmount: {
3685
4041
  type: 'number',
3686
- description: 'Expected amount',
3687
- example: 3000
4042
+ description: 'Expected amount'
3688
4043
  },
3689
- date: {
3690
- type: 'string',
3691
- description: 'Expected date (YYYY-MM-DD)',
3692
- example: '2024-04-01'
4044
+ expectedDay: {
4045
+ type: 'number',
4046
+ description: 'Expected day of month'
3693
4047
  },
3694
- icon: {
3695
- type: 'string',
3696
- description: 'Rule icon emoji',
3697
- example: '🏠',
3698
- nullable: true
4048
+ customIntervalDays: {
4049
+ type: 'number',
4050
+ description: 'Custom interval in days'
3699
4051
  },
3700
4052
  currency: {
3701
4053
  type: 'string',
3702
- description: 'Currency code',
3703
- example: 'CNY'
3704
- }
3705
- },
3706
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3707
- } as const;
3708
-
3709
- export const $MonthlyForecastDto = {
3710
- type: 'object',
3711
- properties: {
3712
- month: {
3713
- type: 'string',
3714
- description: 'Month (YYYY-MM)',
3715
- example: '2024-04'
4054
+ description: 'Currency code'
3716
4055
  },
3717
- expectedOutflow: {
3718
- type: 'number',
3719
- description: 'Total expected outflow for the month',
3720
- example: 8500
4056
+ matchPayeePattern: {
4057
+ type: 'string',
4058
+ description: 'Payee matching pattern'
3721
4059
  },
3722
- itemCount: {
4060
+ matchAmountTolerance: {
3723
4061
  type: 'number',
3724
- description: 'Number of expected transactions',
3725
- example: 3
3726
- },
3727
- byCurrency: {
3728
- type: 'object',
3729
- description: 'Breakdown by currency',
3730
- example: {
3731
- CNY: 8500,
3732
- USD: 100
3733
- }
4062
+ description: 'Amount tolerance percentage'
3734
4063
  },
3735
- items: {
3736
- description: 'Individual forecast items',
3737
- type: 'array',
3738
- items: {
3739
- $ref: '#/components/schemas/ForecastItemDto'
3740
- }
3741
- }
3742
- },
3743
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3744
- } as const;
3745
-
3746
- export const $ForecastResponseDto = {
3747
- type: 'object',
3748
- properties: {
3749
- forecast: {
3750
- description: 'Monthly forecast data',
3751
- type: 'array',
3752
- items: {
3753
- $ref: '#/components/schemas/MonthlyForecastDto'
3754
- }
4064
+ defaultExpenseAccount: {
4065
+ type: 'string',
4066
+ description: 'Default expense account'
3755
4067
  },
3756
- totalOutflow: {
3757
- type: 'number',
3758
- description: 'Total expected outflow across all months',
3759
- example: 25500
4068
+ defaultPaymentAccount: {
4069
+ type: 'string',
4070
+ description: 'Default payment account'
3760
4071
  },
3761
- totalByCurrency: {
3762
- type: 'object',
3763
- description: 'Total by currency across all months',
3764
- example: {
3765
- CNY: 25500,
3766
- USD: 300
3767
- }
4072
+ defaultPayee: {
4073
+ type: 'string',
4074
+ description: 'Default payee'
3768
4075
  },
3769
- rulesCount: {
3770
- type: 'number',
3771
- description: 'Number of active recurring rules included',
3772
- example: 5
4076
+ isActive: {
4077
+ type: 'boolean',
4078
+ description: 'Whether rule is active'
3773
4079
  },
3774
- periodStart: {
4080
+ startDate: {
3775
4081
  type: 'string',
3776
- description: 'Forecast period start date',
3777
- example: '2024-04-01'
4082
+ description: 'Rule start date (YYYY-MM-DD)'
3778
4083
  },
3779
- periodEnd: {
4084
+ endDate: {
3780
4085
  type: 'string',
3781
- description: 'Forecast period end date',
3782
- example: '2024-06-30'
3783
- }
3784
- },
3785
- required: [
3786
- 'forecast',
3787
- 'totalOutflow',
3788
- 'totalByCurrency',
3789
- 'rulesCount',
3790
- 'periodStart',
3791
- 'periodEnd'
3792
- ]
3793
- } as const;
3794
-
3795
- export const $CurrencyBalanceDto = {
3796
- type: 'object',
3797
- properties: {
3798
- currency: {
4086
+ description: 'Rule end date (YYYY-MM-DD)'
4087
+ },
4088
+ autoCreate: {
4089
+ type: 'boolean',
4090
+ description: 'Auto-create transaction on expected date'
4091
+ },
4092
+ lastOccurrence: {
3799
4093
  type: 'string',
3800
- description: 'ISO 4217 currency code',
3801
- example: 'CNY'
4094
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3802
4095
  },
3803
- balance: {
4096
+ totalCount: {
4097
+ type: 'number',
4098
+ description: 'Total matched transactions count'
4099
+ },
4100
+ createdAt: {
4101
+ format: 'date-time',
3804
4102
  type: 'string',
3805
- description: 'Balance amount',
3806
- example: '500000.00'
3807
- }
3808
- },
3809
- required: ['currency', 'balance']
3810
- } as const;
3811
-
3812
- export const $TimeSeriesPointDto = {
3813
- type: 'object',
3814
- properties: {
3815
- date: {
4103
+ description: 'Created at timestamp'
4104
+ },
4105
+ updatedAt: {
4106
+ format: 'date-time',
3816
4107
  type: 'string',
3817
- description: 'Date in YYYY-MM-DD format',
3818
- example: '2024-06-15'
4108
+ description: 'Updated at timestamp'
3819
4109
  },
3820
- value: {
4110
+ pendingCount: {
4111
+ type: 'number',
4112
+ description: 'Number of pending expected transactions'
4113
+ },
4114
+ overdueCount: {
4115
+ type: 'number',
4116
+ description: 'Number of overdue expected transactions'
4117
+ },
4118
+ nextExpectedDate: {
3821
4119
  type: 'string',
3822
- description: 'Value at this date (in base currency)',
3823
- example: '500000.00'
4120
+ description: 'Next expected date (YYYY-MM-DD)'
3824
4121
  },
3825
- change: {
3826
- type: 'object',
3827
- description: 'Change from previous point',
3828
- example: '5000.00'
4122
+ totalAmount: {
4123
+ type: 'number',
4124
+ description: 'Total amount of all matched transactions'
3829
4125
  },
3830
- assets: {
4126
+ averageAmount: {
4127
+ type: 'number',
4128
+ description: 'Average amount per transaction'
4129
+ },
4130
+ transactionCount: {
4131
+ type: 'number',
4132
+ description: 'Number of matched transactions'
4133
+ },
4134
+ firstDate: {
3831
4135
  type: 'string',
3832
- description: 'Total assets at this date (in base currency)',
3833
- example: '494338.00'
4136
+ description: 'First matched transaction date (YYYY-MM-DD)'
3834
4137
  },
3835
- liabilities: {
4138
+ lastDate: {
3836
4139
  type: 'string',
3837
- description: 'Total liabilities at this date (in base currency)',
3838
- example: '310098.00'
4140
+ description: 'Last matched transaction date (YYYY-MM-DD)'
3839
4141
  },
3840
- byCurrency: {
3841
- description: 'Multi-currency breakdown for this point',
3842
- type: 'array',
3843
- items: {
3844
- $ref: '#/components/schemas/CurrencyBalanceDto'
3845
- }
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'
3846
4149
  }
3847
4150
  },
3848
- required: ['date', 'value']
4151
+ required: [
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'
4172
+ ]
3849
4173
  } as const;
3850
4174
 
3851
- export const $TrendSummaryDto = {
4175
+ export const $UpdateRecurringRuleDto = {
3852
4176
  type: 'object',
3853
4177
  properties: {
3854
- startValue: {
4178
+ name: {
3855
4179
  type: 'string',
3856
- description: 'Value at start of period',
3857
- example: '450000.00'
4180
+ description: 'Rule name (unique per user)',
4181
+ maxLength: 100
3858
4182
  },
3859
- endValue: {
4183
+ icon: {
3860
4184
  type: 'string',
3861
- description: 'Value at end of period',
3862
- example: '500000.00'
4185
+ description: 'Icon emoji',
4186
+ maxLength: 10
3863
4187
  },
3864
- totalChange: {
4188
+ frequency: {
3865
4189
  type: 'string',
3866
- description: 'Total change over period',
3867
- example: '50000.00'
4190
+ description: 'Recurring frequency',
4191
+ enum: [
4192
+ 'WEEKLY',
4193
+ 'BIWEEKLY',
4194
+ 'MONTHLY',
4195
+ 'BIMONTHLY',
4196
+ 'QUARTERLY',
4197
+ 'YEARLY',
4198
+ 'CUSTOM'
4199
+ ]
3868
4200
  },
3869
- totalChangePercentage: {
3870
- type: 'string',
3871
- description: 'Total change percentage',
3872
- example: '+11.11%'
3873
- }
3874
- },
3875
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3876
- } as const;
3877
-
3878
- export const $MultiCurrencyPointDto = {
3879
- type: 'object',
3880
- properties: {
3881
- date: {
3882
- type: 'string',
3883
- description: 'Date in YYYY-MM-DD format',
3884
- example: '2024-06-15'
4201
+ expectedAmount: {
4202
+ type: 'number',
4203
+ description: 'Expected amount (positive number)',
4204
+ minimum: 0
3885
4205
  },
3886
- byCurrency: {
3887
- description: 'Balances by currency',
3888
- type: 'array',
3889
- items: {
3890
- $ref: '#/components/schemas/CurrencyBalanceDto'
3891
- }
3892
- }
3893
- },
3894
- required: ['date', 'byCurrency']
3895
- } as const;
3896
-
3897
- export const $PortfolioTrendsResponseDto = {
3898
- type: 'object',
3899
- properties: {
3900
- series: {
3901
- description: 'Time series data points',
3902
- type: 'array',
3903
- items: {
3904
- $ref: '#/components/schemas/TimeSeriesPointDto'
3905
- }
4206
+ expectedDay: {
4207
+ type: 'number',
4208
+ description: 'Expected day of month (1-31)',
4209
+ minimum: 1,
4210
+ maximum: 31
4211
+ },
4212
+ currency: {
4213
+ type: 'string',
4214
+ description: 'Currency code',
4215
+ maxLength: 10
3906
4216
  },
3907
- summary: {
3908
- description: 'Period summary',
3909
- allOf: [
3910
- {
3911
- $ref: '#/components/schemas/TrendSummaryDto'
3912
- }
3913
- ]
4217
+ matchPayeePattern: {
4218
+ type: 'string',
4219
+ description: 'Payee matching pattern (supports wildcards)',
4220
+ maxLength: 200
3914
4221
  },
3915
- period: {
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: {
3916
4230
  type: 'string',
3917
- description: 'Period requested',
3918
- example: '6m'
4231
+ description: 'Default expense account for auto-create',
4232
+ maxLength: 200
3919
4233
  },
3920
- granularity: {
4234
+ defaultPaymentAccount: {
3921
4235
  type: 'string',
3922
- description: 'Data granularity',
3923
- example: 'month'
4236
+ description: 'Default payment account for auto-create',
4237
+ maxLength: 200
3924
4238
  },
3925
- currency: {
4239
+ defaultPayee: {
3926
4240
  type: 'string',
3927
- description: 'Base currency for converted values',
3928
- example: 'CNY'
4241
+ description: 'Default payee for auto-create',
4242
+ maxLength: 200
3929
4243
  },
3930
- byCurrency: {
3931
- description:
3932
- 'Multi-currency time series (each point has currency breakdown)',
3933
- type: 'array',
3934
- items: {
3935
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3936
- }
4244
+ autoCreate: {
4245
+ type: 'boolean',
4246
+ description: 'Auto-create transaction when expected date arrives',
4247
+ default: false
3937
4248
  },
3938
- warnings: {
3939
- description: 'Exchange rate warnings',
3940
- type: 'array',
3941
- items: {
3942
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3943
- }
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'
3944
4261
  }
3945
- },
3946
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4262
+ }
3947
4263
  } as const;
3948
4264
 
3949
- export const $CashFlowPointDto = {
4265
+ export const $ExpectedTransactionRuleDto = {
3950
4266
  type: 'object',
3951
4267
  properties: {
3952
- month: {
4268
+ name: {
3953
4269
  type: 'string',
3954
- description: 'Month key (YYYY-MM)',
3955
- example: '2024-03'
4270
+ description: 'Rule name'
3956
4271
  },
3957
- income: {
4272
+ icon: {
3958
4273
  type: 'string',
3959
- description: 'Income in base currency (absolute, converted)',
3960
- example: '10000.00'
4274
+ description: 'Rule icon'
3961
4275
  },
3962
- expense: {
4276
+ frequency: {
3963
4277
  type: 'string',
3964
- description: 'Expense in base currency (absolute, converted)',
3965
- example: '5000.00'
4278
+ description: 'Rule frequency'
3966
4279
  },
3967
- netSavings: {
4280
+ currency: {
3968
4281
  type: 'string',
3969
- description: 'netSavings = income − expense (savings positive)',
3970
- example: '5000.00'
4282
+ description: 'Currency code'
3971
4283
  }
3972
4284
  },
3973
- required: ['month', 'income', 'expense', 'netSavings']
4285
+ required: ['name', 'frequency', 'currency']
3974
4286
  } as const;
3975
4287
 
3976
- export const $CashFlowTrendSummaryDto = {
4288
+ export const $ExpectedTransactionResponseDto = {
3977
4289
  type: 'object',
3978
4290
  properties: {
3979
- totalIncome: {
4291
+ id: {
3980
4292
  type: 'string',
3981
- description: 'Total income across the period',
3982
- example: '60000.00'
4293
+ description: 'Expected transaction ID'
3983
4294
  },
3984
- totalExpense: {
4295
+ userId: {
3985
4296
  type: 'string',
3986
- description: 'Total expense across the period',
3987
- example: '30000.00'
4297
+ description: 'User ID'
3988
4298
  },
3989
- totalNetSavings: {
4299
+ ruleId: {
3990
4300
  type: 'string',
3991
- description: 'income − expense across the period',
3992
- example: '30000.00'
4301
+ description: 'Associated rule ID'
3993
4302
  },
3994
- averageMonthlyNetSavings: {
4303
+ expectedDate: {
3995
4304
  type: 'string',
3996
- description:
3997
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3998
- example: '5000.00'
3999
- }
4000
- },
4001
- required: [
4002
- 'totalIncome',
4003
- 'totalExpense',
4004
- 'totalNetSavings',
4005
- 'averageMonthlyNetSavings'
4006
- ]
4007
- } as const;
4008
-
4009
- export const $CashFlowTrendsResponseDto = {
4010
- type: 'object',
4011
- properties: {
4012
- series: {
4013
- description:
4014
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4015
- type: 'array',
4016
- items: {
4017
- $ref: '#/components/schemas/CashFlowPointDto'
4018
- }
4305
+ description: 'Expected date (YYYY-MM-DD)'
4019
4306
  },
4020
- summary: {
4021
- description: 'Period totals',
4307
+ expectedAmount: {
4308
+ type: 'number',
4309
+ description: 'Expected amount'
4310
+ },
4311
+ status: {
4312
+ type: 'string',
4313
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4314
+ },
4315
+ matchedTransactionId: {
4316
+ type: 'string',
4317
+ description: 'Matched transaction ID'
4318
+ },
4319
+ matchedAt: {
4320
+ type: 'string',
4321
+ description: 'Match timestamp (ISO 8601)'
4322
+ },
4323
+ matchConfidence: {
4324
+ type: 'number',
4325
+ description: 'Match confidence score (0-1)'
4326
+ },
4327
+ isOverdue: {
4328
+ type: 'boolean',
4329
+ description: 'Whether this expected transaction is overdue'
4330
+ },
4331
+ rule: {
4332
+ description: 'Rule information',
4022
4333
  allOf: [
4023
4334
  {
4024
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4335
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4025
4336
  }
4026
4337
  ]
4027
4338
  },
4028
- period: {
4029
- type: 'string',
4030
- description: 'Period requested',
4031
- example: '6m'
4032
- },
4033
- granularity: {
4339
+ createdAt: {
4340
+ format: 'date-time',
4034
4341
  type: 'string',
4035
- description: 'Data granularity (v1 returns month buckets)',
4036
- example: 'month'
4342
+ description: 'Created at timestamp'
4037
4343
  },
4038
- currency: {
4344
+ updatedAt: {
4345
+ format: 'date-time',
4039
4346
  type: 'string',
4040
- description: 'Base currency for converted values',
4041
- example: 'CNY'
4042
- },
4043
- warnings: {
4044
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4045
- type: 'array',
4046
- items: {
4047
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4048
- }
4347
+ description: 'Updated at timestamp'
4049
4348
  }
4050
4349
  },
4051
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4052
- } as const;
4053
-
4054
- export const $GenerateSnapshotBody = {
4055
- type: 'object',
4056
- properties: {}
4057
- } as const;
4058
-
4059
- export const $GenerateSnapshotResponse = {
4060
- type: 'object',
4061
- properties: {}
4062
- } as const;
4063
-
4064
- export const $BackfillSnapshotsBody = {
4065
- type: 'object',
4066
- properties: {}
4067
- } as const;
4068
-
4069
- export const $BackfillSnapshotsResponse = {
4070
- type: 'object',
4071
- properties: {}
4350
+ required: [
4351
+ 'id',
4352
+ 'userId',
4353
+ 'ruleId',
4354
+ 'expectedDate',
4355
+ 'expectedAmount',
4356
+ 'status',
4357
+ 'isOverdue',
4358
+ 'rule',
4359
+ 'createdAt',
4360
+ 'updatedAt'
4361
+ ]
4072
4362
  } as const;
4073
4363
 
4074
- export const $DeleteOwnUserDto = {
4364
+ export const $ExpectedTransactionListResponseDto = {
4075
4365
  type: 'object',
4076
4366
  properties: {
4077
- accessToken: {
4078
- type: 'string',
4079
- description: 'Access token for user verification',
4080
- 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'
4081
4376
  }
4082
4377
  },
4083
- required: ['accessToken']
4378
+ required: ['items', 'total']
4084
4379
  } as const;
4085
4380
 
4086
- export const $SignupDto = {
4381
+ export const $ConfirmMatchDto = {
4087
4382
  type: 'object',
4088
4383
  properties: {
4089
- turnstileToken: {
4384
+ transactionId: {
4090
4385
  type: 'string',
4091
- description:
4092
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4093
- example: '0.abc123def456...'
4386
+ description: 'Transaction ID to match with'
4094
4387
  }
4095
- }
4388
+ },
4389
+ required: ['transactionId']
4096
4390
  } as const;
4097
4391
 
4098
- export const $SignupResponseDto = {
4392
+ export const $EnterNowDto = {
4099
4393
  type: 'object',
4100
4394
  properties: {
4101
- authToken: {
4395
+ expenseAccount: {
4396
+ type: 'string',
4397
+ description:
4398
+ 'Override expense account (uses rule default if not provided)',
4399
+ maxLength: 200
4400
+ },
4401
+ paymentAccount: {
4102
4402
  type: 'string',
4103
- description: 'JWT auth token',
4104
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4403
+ description:
4404
+ 'Override payment account (uses rule default if not provided)',
4405
+ maxLength: 200
4105
4406
  },
4106
- accessToken: {
4407
+ amount: {
4408
+ type: 'number',
4409
+ description: 'Override amount (uses expected amount if not provided)',
4410
+ minimum: 0
4411
+ },
4412
+ payee: {
4107
4413
  type: 'string',
4108
- description: 'Auto-generated access token'
4414
+ description: 'Override payee (uses rule default if not provided)',
4415
+ maxLength: 200
4109
4416
  },
4110
- role: {
4417
+ narration: {
4111
4418
  type: 'string',
4112
- description: 'Assigned user role',
4113
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4419
+ description: 'Optional narration',
4420
+ maxLength: 500
4114
4421
  }
4115
- },
4116
- required: ['authToken', 'accessToken', 'role']
4422
+ }
4117
4423
  } as const;
4118
4424
 
4119
- export const $UpdateUserSettingDto = {
4425
+ export const $ForecastItemDto = {
4120
4426
  type: 'object',
4121
4427
  properties: {
4122
- secId: {
4123
- type: 'number',
4124
- description: 'Security ID'
4428
+ rule: {
4429
+ type: 'string',
4430
+ description: 'Rule name',
4431
+ example: 'Rent'
4125
4432
  },
4126
- annualInterestRate: {
4433
+ ruleId: {
4434
+ type: 'string',
4435
+ description: 'Rule ID',
4436
+ example: 'clx123...'
4437
+ },
4438
+ amount: {
4127
4439
  type: 'number',
4128
- description: 'Annual interest rate',
4129
- example: 0.05
4440
+ description: 'Expected amount',
4441
+ example: 3000
4130
4442
  },
4131
- currency: {
4443
+ date: {
4132
4444
  type: 'string',
4133
- description: 'Currency code',
4134
- example: 'USD'
4445
+ description: 'Expected date (YYYY-MM-DD)',
4446
+ example: '2024-04-01'
4135
4447
  },
4136
- baseCurrency: {
4448
+ icon: {
4137
4449
  type: 'string',
4138
- description: 'Base currency code',
4139
- example: 'USD'
4450
+ description: 'Rule icon emoji',
4451
+ example: '🏠',
4452
+ nullable: true
4140
4453
  },
4141
- benchmark: {
4454
+ currency: {
4142
4455
  type: 'string',
4143
- description: 'Benchmark symbol',
4144
- example: 'SPY'
4145
- },
4146
- 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: {
4147
4467
  type: 'string',
4148
- description: 'Color scheme',
4149
- enum: ['DARK', 'LIGHT']
4468
+ description: 'Month (YYYY-MM)',
4469
+ example: '2024-04'
4150
4470
  },
4151
- dateRange: {
4152
- type: 'string',
4153
- description: 'Date range filter',
4154
- example: '1y'
4471
+ expectedOutflow: {
4472
+ type: 'number',
4473
+ description: 'Total expected outflow for the month',
4474
+ example: 8500
4155
4475
  },
4156
- emergencyFund: {
4476
+ itemCount: {
4157
4477
  type: 'number',
4158
- description: 'Emergency fund amount',
4159
- example: 10000
4478
+ description: 'Number of expected transactions',
4479
+ example: 3
4160
4480
  },
4161
- 'filters.accounts': {
4162
- description: 'Account filter IDs',
4163
- type: 'array',
4164
- items: {
4165
- type: 'string'
4481
+ byCurrency: {
4482
+ type: 'object',
4483
+ description: 'Breakdown by currency',
4484
+ example: {
4485
+ CNY: 8500,
4486
+ USD: 100
4166
4487
  }
4167
4488
  },
4168
- 'filters.assetClasses': {
4169
- description: 'Asset class filters',
4489
+ items: {
4490
+ description: 'Individual forecast items',
4170
4491
  type: 'array',
4171
4492
  items: {
4172
- type: 'string'
4493
+ $ref: '#/components/schemas/ForecastItemDto'
4173
4494
  }
4174
- },
4175
- 'filters.dataSource': {
4176
- type: 'string',
4177
- description: 'Data source filter'
4178
- },
4179
- 'filters.symbol': {
4180
- type: 'string',
4181
- description: 'Symbol filter'
4182
- },
4183
- 'filters.tags': {
4184
- description: 'Tag filters',
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',
4185
4505
  type: 'array',
4186
4506
  items: {
4187
- type: 'string'
4507
+ $ref: '#/components/schemas/MonthlyForecastDto'
4188
4508
  }
4189
4509
  },
4190
- isExperimentalFeatures: {
4191
- type: 'boolean',
4192
- description: 'Enable experimental features'
4193
- },
4194
- isRestrictedView: {
4195
- type: 'boolean',
4196
- description: 'Enable restricted view mode'
4197
- },
4198
- language: {
4199
- type: 'string',
4200
- description: 'Language code',
4201
- example: 'en'
4202
- },
4203
- locale: {
4204
- type: 'string',
4205
- description: 'Locale code',
4206
- example: 'en-US'
4207
- },
4208
- projectedTotalAmount: {
4510
+ totalOutflow: {
4209
4511
  type: 'number',
4210
- description: 'Projected total amount',
4211
- example: 1000000
4512
+ description: 'Total expected outflow across all months',
4513
+ example: 25500
4212
4514
  },
4213
- retirementDate: {
4214
- type: 'string',
4215
- description: 'Retirement date in ISO 8601 format',
4216
- 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
+ }
4217
4522
  },
4218
- savingsRate: {
4523
+ rulesCount: {
4219
4524
  type: 'number',
4220
- description: 'Savings rate percentage',
4221
- example: 0.2
4525
+ description: 'Number of active recurring rules included',
4526
+ example: 5
4222
4527
  },
4223
- viewMode: {
4528
+ periodStart: {
4224
4529
  type: 'string',
4225
- description: 'View mode',
4226
- enum: ['DEFAULT', 'ZEN']
4227
- }
4228
- }
4229
- } as const;
4230
-
4231
- export const $UpdatePropertyDto = {
4232
- type: 'object',
4233
- properties: {
4234
- value: {
4530
+ description: 'Forecast period start date',
4531
+ example: '2024-04-01'
4532
+ },
4533
+ periodEnd: {
4235
4534
  type: 'string',
4236
- description: 'Property value'
4535
+ description: 'Forecast period end date',
4536
+ example: '2024-06-30'
4237
4537
  }
4238
4538
  },
4239
- required: ['value']
4539
+ required: [
4540
+ 'forecast',
4541
+ 'totalOutflow',
4542
+ 'totalByCurrency',
4543
+ 'rulesCount',
4544
+ 'periodStart',
4545
+ 'periodEnd'
4546
+ ]
4240
4547
  } as const;
4241
4548
 
4242
4549
  export const $CreateTransactionRuleDto = {
@@ -4798,7 +5105,8 @@ export const $UpdateTransactionRuleDto = {
4798
5105
  },
4799
5106
  matchLogic: {
4800
5107
  type: 'string',
4801
- enum: ['OR', 'AND']
5108
+ enum: ['OR', 'AND'],
5109
+ default: 'OR'
4802
5110
  },
4803
5111
  amountMin: {
4804
5112
  type: 'number',
@@ -4812,13 +5120,10 @@ export const $UpdateTransactionRuleDto = {
4812
5120
  },
4813
5121
  priority: {
4814
5122
  type: 'number',
5123
+ default: 50,
4815
5124
  minimum: 0,
4816
5125
  maximum: 1000
4817
5126
  },
4818
- enabled: {
4819
- type: 'boolean',
4820
- description: 'Enable or disable the rule'
4821
- },
4822
5127
  additionalTags: {
4823
5128
  items: {
4824
5129
  type: 'array'
@@ -4828,6 +5133,10 @@ export const $UpdateTransactionRuleDto = {
4828
5133
  },
4829
5134
  additionalMetadata: {
4830
5135
  type: 'object'
5136
+ },
5137
+ enabled: {
5138
+ type: 'boolean',
5139
+ description: 'Enable or disable the rule'
4831
5140
  }
4832
5141
  }
4833
5142
  } as const;
@@ -4870,22 +5179,85 @@ export const $TestRuleResponseDto = {
4870
5179
  type: 'boolean',
4871
5180
  description: 'Whether the rule matched the test data'
4872
5181
  },
4873
- confidence: {
5182
+ confidence: {
5183
+ type: 'number',
5184
+ description: 'Match confidence score (0-1)',
5185
+ example: 0.85
5186
+ },
5187
+ matchDetails: {
5188
+ type: 'object',
5189
+ description: 'Details of which fields matched',
5190
+ example: {
5191
+ narration: true,
5192
+ payee: false,
5193
+ categoryAccount: false
5194
+ }
5195
+ }
5196
+ },
5197
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5198
+ } as const;
5199
+
5200
+ export const $CategoryCatalogEntryDto = {
5201
+ type: 'object',
5202
+ properties: {
5203
+ slug: {
5204
+ type: 'string',
5205
+ description: 'Category slug (single source-of-truth)',
5206
+ example: 'food'
5207
+ },
5208
+ scenario: {
5209
+ type: 'string',
5210
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5211
+ enum: [
5212
+ 'expense',
5213
+ 'income',
5214
+ 'investment',
5215
+ 'banking',
5216
+ 'transfer',
5217
+ 'payment'
5218
+ ],
5219
+ example: 'expense'
5220
+ },
5221
+ icon: {
5222
+ type: 'string',
5223
+ description: 'Lucide icon name',
5224
+ example: 'utensils'
5225
+ },
5226
+ regions: {
5227
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5228
+ example: ['*'],
5229
+ type: 'array',
5230
+ items: {
5231
+ type: 'string'
5232
+ }
5233
+ }
5234
+ },
5235
+ required: ['slug', 'scenario', 'icon', 'regions']
5236
+ } as const;
5237
+
5238
+ export const $CategoryCatalogListResponseDto = {
5239
+ type: 'object',
5240
+ properties: {
5241
+ items: {
5242
+ description: 'Category entries (region-scoped, query-filtered)',
5243
+ type: 'array',
5244
+ items: {
5245
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5246
+ }
5247
+ },
5248
+ total: {
4874
5249
  type: 'number',
4875
- description: 'Match confidence score (0-1)',
4876
- example: 0.85
5250
+ description:
5251
+ 'Total category entries for the region (before query filtering)',
5252
+ example: 30
4877
5253
  },
4878
- matchDetails: {
4879
- type: 'object',
4880
- description: 'Details of which fields matched',
4881
- example: {
4882
- narration: true,
4883
- payee: false,
4884
- categoryAccount: false
4885
- }
5254
+ region: {
5255
+ type: 'string',
5256
+ description: 'Region code',
5257
+ example: 'cn'
4886
5258
  }
4887
5259
  },
4888
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5260
+ required: ['items', 'total', 'region']
4889
5261
  } as const;
4890
5262
 
4891
5263
  export const $CreateBeanEventDto = {
@@ -5051,6 +5423,14 @@ export const $OnboardingAccountDto = {
5051
5423
  description:
5052
5424
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5053
5425
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5426
+ },
5427
+ displayName: {
5428
+ type: 'string',
5429
+ description:
5430
+ 'User-set display name override (omit/null = keep the derived name)',
5431
+ nullable: true,
5432
+ maxLength: 50,
5433
+ example: 'Salary card'
5054
5434
  }
5055
5435
  },
5056
5436
  required: ['path', 'currency']
@@ -5630,7 +6010,8 @@ export const $UpdateMapperDefaultsDto = {
5630
6010
  type: 'string',
5631
6011
  description: 'Source account for transactions (Beancount format)',
5632
6012
  example: 'Assets:CN:Alipay:Balance',
5633
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6013
+ pattern:
6014
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5634
6015
  },
5635
6016
  currency: {
5636
6017
  type: 'string',
@@ -5644,13 +6025,15 @@ export const $UpdateMapperDefaultsDto = {
5644
6025
  type: 'string',
5645
6026
  description: 'Default expense account (optional)',
5646
6027
  example: 'Expenses:Unknown',
5647
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6028
+ pattern:
6029
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5648
6030
  },
5649
6031
  incomeAccount: {
5650
6032
  type: 'string',
5651
6033
  description: 'Default income account (optional)',
5652
6034
  example: 'Income:Unknown',
5653
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6035
+ pattern:
6036
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5654
6037
  },
5655
6038
  methodAccountMapping: {
5656
6039
  type: 'object',
@@ -5707,12 +6090,14 @@ export const $ProviderSyncConfigDto = {
5707
6090
  },
5708
6091
  defaultExpenseAccount: {
5709
6092
  type: 'string',
5710
- description: 'Default expense account for the second posting',
6093
+ description:
6094
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5711
6095
  example: 'Expenses:Unknown'
5712
6096
  },
5713
6097
  defaultIncomeAccount: {
5714
6098
  type: 'string',
5715
- description: 'Default income account for the second posting',
6099
+ description:
6100
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5716
6101
  example: 'Income:Unknown'
5717
6102
  },
5718
6103
  filterPending: {
@@ -5727,12 +6112,7 @@ export const $ProviderSyncConfigDto = {
5727
6112
  example: 'acc_gocardless_001'
5728
6113
  }
5729
6114
  },
5730
- required: [
5731
- 'sourceAccount',
5732
- 'defaultCurrency',
5733
- 'defaultExpenseAccount',
5734
- 'defaultIncomeAccount'
5735
- ]
6115
+ required: ['sourceAccount', 'defaultCurrency']
5736
6116
  } as const;
5737
6117
 
5738
6118
  export const $ProviderSyncDto = {
@@ -5942,15 +6322,105 @@ export const $UncoveredFormatMissDto = {
5942
6322
  properties: {}
5943
6323
  } as const;
5944
6324
 
6325
+ export const $ClientParsedDataDto = {
6326
+ type: 'object',
6327
+ properties: {
6328
+ amount: {
6329
+ type: 'number',
6330
+ description: 'Transaction amount',
6331
+ example: 35
6332
+ },
6333
+ currency: {
6334
+ type: 'string',
6335
+ description: 'Currency code',
6336
+ example: 'CNY'
6337
+ },
6338
+ date: {
6339
+ type: 'string',
6340
+ description: 'Transaction date (ISO 8601)',
6341
+ example: '2026-08-15'
6342
+ },
6343
+ payee: {
6344
+ type: 'string',
6345
+ description: 'Payee/merchant name',
6346
+ example: 'Starbucks'
6347
+ },
6348
+ narration: {
6349
+ type: 'string',
6350
+ description: 'Transaction narration'
6351
+ },
6352
+ category: {
6353
+ type: 'string',
6354
+ description: 'Category slug',
6355
+ example: 'food_restaurant'
6356
+ },
6357
+ incomeType: {
6358
+ type: 'string',
6359
+ description: 'Income type',
6360
+ example: 'Salary'
6361
+ },
6362
+ incomeSource: {
6363
+ type: 'string',
6364
+ description: 'Income source',
6365
+ example: 'Anthropic Inc.'
6366
+ },
6367
+ symbol: {
6368
+ type: 'string',
6369
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6370
+ example: 'AAPL'
6371
+ },
6372
+ quantity: {
6373
+ type: 'number',
6374
+ description: 'Quantity of shares/units',
6375
+ example: 100
6376
+ },
6377
+ price: {
6378
+ type: 'number',
6379
+ description: 'Unit price per share/unit',
6380
+ example: 1900
6381
+ },
6382
+ investmentAction: {
6383
+ type: 'string',
6384
+ description: 'Investment action',
6385
+ enum: ['buy', 'sell'],
6386
+ example: 'buy'
6387
+ },
6388
+ paymentSource: {
6389
+ type: 'string',
6390
+ description: 'Payment source: asset (default) or liability (credit card)',
6391
+ enum: ['asset', 'liability'],
6392
+ example: 'asset'
6393
+ },
6394
+ liabilityHint: {
6395
+ type: 'string',
6396
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6397
+ example: 'CreditCard'
6398
+ },
6399
+ warning: {
6400
+ type: 'string',
6401
+ description:
6402
+ 'Display-only warning from the prior response; accepted but ignored.',
6403
+ example: 'Cross-currency settlement applies.'
6404
+ }
6405
+ }
6406
+ } as const;
6407
+
5945
6408
  export const $ProcessNlpDto = {
5946
6409
  type: 'object',
5947
6410
  properties: {
5948
6411
  message: {
5949
6412
  type: 'string',
5950
- description: 'Natural language text describing a transaction (Chinese)',
5951
- example: 'yesterday Starbucks spent 35 yuan',
6413
+ description:
6414
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6415
+ example: 'Starbucks 35',
5952
6416
  maxLength: 500
5953
6417
  },
6418
+ confirm: {
6419
+ type: 'boolean',
6420
+ description:
6421
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6422
+ example: true
6423
+ },
5954
6424
  sessionId: {
5955
6425
  type: 'string',
5956
6426
  description:
@@ -5958,14 +6428,18 @@ export const $ProcessNlpDto = {
5958
6428
  example: 'session_abc123'
5959
6429
  },
5960
6430
  parsedData: {
5961
- type: 'object',
5962
6431
  description:
5963
6432
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5964
6433
  example: {
5965
6434
  amount: 35,
5966
6435
  currency: 'CNY',
5967
6436
  payee: 'Starbucks'
5968
- }
6437
+ },
6438
+ allOf: [
6439
+ {
6440
+ $ref: '#/components/schemas/ClientParsedDataDto'
6441
+ }
6442
+ ]
5969
6443
  },
5970
6444
  selectedRuleId: {
5971
6445
  type: 'string',
@@ -5976,11 +6450,29 @@ export const $ProcessNlpDto = {
5976
6450
  selectedAccount: {
5977
6451
  type: 'string',
5978
6452
  description:
5979
- 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i], or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6453
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i].path, or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
5980
6454
  example: 'Expenses:Food:Coffee'
6455
+ },
6456
+ viewpointAccount: {
6457
+ type: 'string',
6458
+ description:
6459
+ '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.',
6460
+ example: 'Assets:CN:Bank:ICBC'
6461
+ },
6462
+ viewpointCategory: {
6463
+ type: 'string',
6464
+ description:
6465
+ "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.",
6466
+ example: 'Food'
6467
+ },
6468
+ viewpointFlow: {
6469
+ type: 'string',
6470
+ description:
6471
+ "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.",
6472
+ enum: ['income', 'expense'],
6473
+ example: 'expense'
5981
6474
  }
5982
- },
5983
- required: ['message']
6475
+ }
5984
6476
  } as const;
5985
6477
 
5986
6478
  export const $NlpTransactionInfoDto = {
@@ -6292,6 +6784,24 @@ export const $NlpRuleConfirmationDataDto = {
6292
6784
  ]
6293
6785
  } as const;
6294
6786
 
6787
+ export const $NlpAccountCandidateDto = {
6788
+ type: 'object',
6789
+ properties: {
6790
+ path: {
6791
+ type: 'string',
6792
+ description: 'Canonical beancount account path (echo back on selection)',
6793
+ example: 'Expenses:Food:Dining'
6794
+ },
6795
+ name: {
6796
+ type: 'string',
6797
+ description:
6798
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6799
+ example: '餐饮'
6800
+ }
6801
+ },
6802
+ required: ['path', 'name']
6803
+ } as const;
6804
+
6295
6805
  export const $NlpAccountConfirmationDataDto = {
6296
6806
  type: 'object',
6297
6807
  properties: {
@@ -6307,10 +6817,11 @@ export const $NlpAccountConfirmationDataDto = {
6307
6817
  example: 'Expenses:Food:Drinks'
6308
6818
  },
6309
6819
  similarAccounts: {
6310
- description: 'Similar accounts for user selection',
6820
+ description:
6821
+ 'Similar accounts for user selection (path + localized name, #680)',
6311
6822
  type: 'array',
6312
6823
  items: {
6313
- type: 'string'
6824
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6314
6825
  }
6315
6826
  },
6316
6827
  errorMessage: {
@@ -6497,7 +7008,8 @@ export const $NlpSuggestedAccountDto = {
6497
7008
  },
6498
7009
  confidence: {
6499
7010
  type: 'number',
6500
- description: 'Confidence score for this suggestion (0-1)',
7011
+ description:
7012
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
6501
7013
  example: 0.9
6502
7014
  }
6503
7015
  },
@@ -6533,23 +7045,31 @@ export const $NlpDefaultAccountsDto = {
6533
7045
  properties: {
6534
7046
  asset: {
6535
7047
  type: 'string',
6536
- description: 'Default asset account',
6537
- example: 'Assets:Checking'
7048
+ description:
7049
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7050
+ example: 'Assets:Checking',
7051
+ nullable: true
6538
7052
  },
6539
7053
  expense: {
6540
7054
  type: 'string',
6541
- description: 'Default expense account',
6542
- example: 'Expenses:Uncategorized'
7055
+ description:
7056
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7057
+ example: 'Expenses:Food:Coffee',
7058
+ nullable: true
6543
7059
  },
6544
7060
  income: {
6545
7061
  type: 'string',
6546
- description: 'Default income account',
6547
- example: 'Income:Uncategorized'
7062
+ description:
7063
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7064
+ example: 'Income:Salary',
7065
+ nullable: true
6548
7066
  },
6549
7067
  liability: {
6550
7068
  type: 'string',
6551
- description: 'Default liability account',
6552
- example: 'Liabilities:CreditCard'
7069
+ description:
7070
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7071
+ example: 'Liabilities:CreditCard',
7072
+ nullable: true
6553
7073
  }
6554
7074
  },
6555
7075
  required: ['asset', 'expense', 'income', 'liability']
@@ -6589,7 +7109,7 @@ export const $NlpResponseDto = {
6589
7109
  type: 'string',
6590
7110
  description:
6591
7111
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6592
- enum: ['transfer', 'banking', 'investment'],
7112
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6593
7113
  example: 'investment'
6594
7114
  },
6595
7115
  liabilitySubType: {
@@ -6737,7 +7257,7 @@ export const $NlpResponseDto = {
6737
7257
  },
6738
7258
  suggestedAccounts: {
6739
7259
  description:
6740
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
7260
+ 'Suggested accounts for this transaction (#586). confirm/confirm_rule/confirm_account: predicted (source/destination carry confidence); created: actual persisted accounts (confidence omitted). confirm_account destination is the suggested replacement, never the invalid account.',
6741
7261
  allOf: [
6742
7262
  {
6743
7263
  $ref: '#/components/schemas/NlpSuggestedAccountsDto'
@@ -6746,7 +7266,7 @@ export const $NlpResponseDto = {
6746
7266
  },
6747
7267
  defaultAccounts: {
6748
7268
  description:
6749
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
7269
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
6750
7270
  allOf: [
6751
7271
  {
6752
7272
  $ref: '#/components/schemas/NlpDefaultAccountsDto'
@@ -6792,13 +7312,26 @@ export const $PlatformListItemDto = {
6792
7312
  suggestedSegment: {
6793
7313
  type: 'string',
6794
7314
  description:
6795
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7315
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6796
7316
  },
6797
7317
  logoUrl: {
6798
7318
  type: 'string',
6799
7319
  description: 'Logo URL',
6800
7320
  nullable: true
6801
7321
  },
7322
+ countryCode: {
7323
+ type: 'string',
7324
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7325
+ example: 'CN',
7326
+ nullable: true
7327
+ },
7328
+ category: {
7329
+ type: 'string',
7330
+ description:
7331
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7332
+ nullable: true,
7333
+ example: 'DigitalWallet'
7334
+ },
6802
7335
  isBound: {
6803
7336
  type: 'boolean',
6804
7337
  description: 'Whether user has accounts using this platform'
@@ -6812,6 +7345,8 @@ export const $PlatformListItemDto = {
6812
7345
  'canonical',
6813
7346
  'suggestedSegment',
6814
7347
  'logoUrl',
7348
+ 'countryCode',
7349
+ 'category',
6815
7350
  'isBound'
6816
7351
  ]
6817
7352
  } as const;
@@ -6844,59 +7379,147 @@ export const $PlatformMatchResultDto = {
6844
7379
  'OTHER'
6845
7380
  ]
6846
7381
  },
6847
- suggestedSegment: {
7382
+ suggestedSegment: {
7383
+ type: 'string',
7384
+ description:
7385
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7386
+ },
7387
+ logoUrl: {
7388
+ type: 'string',
7389
+ description: 'Logo URL',
7390
+ nullable: true
7391
+ },
7392
+ countryCode: {
7393
+ type: 'string',
7394
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7395
+ example: 'CN',
7396
+ nullable: true
7397
+ },
7398
+ category: {
7399
+ type: 'string',
7400
+ description:
7401
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7402
+ nullable: true,
7403
+ example: 'DigitalWallet'
7404
+ },
7405
+ matchType: {
7406
+ type: 'string',
7407
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7408
+ enum: ['exact', 'prefix', 'substring']
7409
+ }
7410
+ },
7411
+ required: [
7412
+ 'id',
7413
+ 'name',
7414
+ 'canonical',
7415
+ 'type',
7416
+ 'suggestedSegment',
7417
+ 'logoUrl',
7418
+ 'countryCode',
7419
+ 'category',
7420
+ 'matchType'
7421
+ ]
7422
+ } as const;
7423
+
7424
+ export const $PlatformMatchResponseDto = {
7425
+ type: 'object',
7426
+ properties: {
7427
+ platforms: {
7428
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7429
+ type: 'array',
7430
+ items: {
7431
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7432
+ }
7433
+ },
7434
+ matchType: {
7435
+ type: 'string',
7436
+ description:
7437
+ "Overall match quality — top row's tier, or 'none' when no hits",
7438
+ enum: ['none', 'exact', 'prefix', 'substring']
7439
+ },
7440
+ total: {
7441
+ type: 'number',
7442
+ description: 'Total matches before LIMIT (truncation transparency)'
7443
+ },
7444
+ hasMore: {
7445
+ type: 'boolean',
7446
+ description: 'true when total > platforms.length (more matches exist)'
7447
+ }
7448
+ },
7449
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7450
+ } as const;
7451
+
7452
+ export const $PlatformStandardsPlatformDto = {
7453
+ type: 'object',
7454
+ properties: {
7455
+ id: {
7456
+ type: 'string',
7457
+ description: 'Global platform ID'
7458
+ },
7459
+ name: {
7460
+ type: 'string',
7461
+ description: 'Platform name (e.g., "ICBC")'
7462
+ },
7463
+ canonical: {
7464
+ type: 'string',
7465
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7466
+ },
7467
+ suggestedSegment: {
7468
+ type: 'string',
7469
+ description:
7470
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7471
+ },
7472
+ type: {
7473
+ type: 'string',
7474
+ description: 'Platform type',
7475
+ enum: [
7476
+ 'BANK',
7477
+ 'BROKERAGE',
7478
+ 'CRYPTO_EXCHANGE',
7479
+ 'PAYMENT',
7480
+ 'INVESTMENT',
7481
+ 'INSURANCE',
7482
+ 'OTHER'
7483
+ ]
7484
+ },
7485
+ category: {
6848
7486
  type: 'string',
6849
7487
  description:
6850
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
6851
- },
6852
- logoUrl: {
6853
- type: 'string',
6854
- description: 'Logo URL',
6855
- nullable: true
6856
- },
6857
- matchType: {
6858
- type: 'string',
6859
- description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6860
- enum: ['exact', 'prefix', 'substring']
7488
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7489
+ nullable: true,
7490
+ example: 'Bank'
6861
7491
  }
6862
7492
  },
6863
- required: [
6864
- 'id',
6865
- 'name',
6866
- 'canonical',
6867
- 'type',
6868
- 'suggestedSegment',
6869
- 'logoUrl',
6870
- 'matchType'
6871
- ]
7493
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
6872
7494
  } as const;
6873
7495
 
6874
- export const $PlatformMatchResponseDto = {
7496
+ export const $PlatformStandardsResponseDto = {
6875
7497
  type: 'object',
6876
7498
  properties: {
6877
- platforms: {
6878
- description: 'Ranked matches, best tier first (at most 10 rows)',
6879
- type: 'array',
6880
- items: {
6881
- $ref: '#/components/schemas/PlatformMatchResultDto'
6882
- }
7499
+ platform: {
7500
+ description: 'The selected platform (institution lock source)',
7501
+ allOf: [
7502
+ {
7503
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7504
+ }
7505
+ ]
6883
7506
  },
6884
- matchType: {
7507
+ region: {
6885
7508
  type: 'string',
6886
7509
  description:
6887
- "Overall match quality — top row's tier, or 'none' when no hits",
6888
- enum: ['none', 'exact', 'prefix', 'substring']
6889
- },
6890
- total: {
6891
- type: 'number',
6892
- description: 'Total matches before LIMIT (truncation transparency)'
7510
+ "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.",
7511
+ example: 'CN'
6893
7512
  },
6894
- hasMore: {
6895
- type: 'boolean',
6896
- description: 'true when total > platforms.length (more matches exist)'
7513
+ templates: {
7514
+ description:
7515
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7516
+ type: 'array',
7517
+ items: {
7518
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7519
+ }
6897
7520
  }
6898
7521
  },
6899
- required: ['platforms', 'matchType', 'total', 'hasMore']
7522
+ required: ['platform', 'region', 'templates']
6900
7523
  } as const;
6901
7524
 
6902
7525
  export const $CreatePlatformDto = {
@@ -7000,7 +7623,8 @@ export const $UpdatePlatformDto = {
7000
7623
  },
7001
7624
  isActive: {
7002
7625
  type: 'boolean',
7003
- description: 'Whether the platform is active'
7626
+ description: 'Whether the platform is active',
7627
+ default: true
7004
7628
  }
7005
7629
  }
7006
7630
  } as const;
@@ -7163,7 +7787,8 @@ export const $AccountItemDto = {
7163
7787
  },
7164
7788
  displayName: {
7165
7789
  type: 'string',
7166
- description: 'Display name (last part of account path)',
7790
+ description:
7791
+ '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)',
7167
7792
  example: 'Savings'
7168
7793
  },
7169
7794
  balance: {
@@ -7199,7 +7824,8 @@ export const $PlatformGroupDto = {
7199
7824
  example: 'CMB Bank'
7200
7825
  },
7201
7826
  accounts: {
7202
- description: 'Accounts within this platform',
7827
+ description:
7828
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7203
7829
  type: 'array',
7204
7830
  items: {
7205
7831
  $ref: '#/components/schemas/AccountItemDto'
@@ -7207,7 +7833,8 @@ export const $PlatformGroupDto = {
7207
7833
  },
7208
7834
  totalBalance: {
7209
7835
  type: 'string',
7210
- description: 'FX-converted total balance in base currency',
7836
+ description:
7837
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7211
7838
  example: '100000.00'
7212
7839
  },
7213
7840
  balanceByCurrency: {
@@ -7226,7 +7853,7 @@ export const $PlatformGroupDto = {
7226
7853
  sharePct: {
7227
7854
  type: 'number',
7228
7855
  description:
7229
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7856
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7230
7857
  example: 42.5
7231
7858
  }
7232
7859
  },
@@ -7274,7 +7901,8 @@ export const $AccountsSummaryDto = {
7274
7901
  properties: {
7275
7902
  totalAccounts: {
7276
7903
  type: 'number',
7277
- description: 'Total number of accounts'
7904
+ description:
7905
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7278
7906
  },
7279
7907
  totalPlatforms: {
7280
7908
  type: 'number',
@@ -7332,7 +7960,8 @@ export const $AccountItemWithAssetClassDto = {
7332
7960
  },
7333
7961
  displayName: {
7334
7962
  type: 'string',
7335
- description: 'Display name (last part of account path)',
7963
+ description:
7964
+ '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)',
7336
7965
  example: 'Savings'
7337
7966
  },
7338
7967
  balance: {
@@ -7818,7 +8447,7 @@ export const $MonetaryDto = {
7818
8447
  example: 'USD'
7819
8448
  },
7820
8449
  baseCcyEquivalent: {
7821
- type: 'object',
8450
+ type: 'string',
7822
8451
  description: 'Converted to user base currency (Decimal string)',
7823
8452
  example: '21600',
7824
8453
  nullable: true
@@ -7894,13 +8523,13 @@ export const $HoldingPnlRowDto = {
7894
8523
  example: 'Assets:US:Broker:AAPL'
7895
8524
  },
7896
8525
  accountCcy: {
7897
- type: 'object',
8526
+ type: 'string',
7898
8527
  description: 'Account settlement currency (ISO 4217), from cost currency',
7899
8528
  nullable: true,
7900
8529
  example: 'USD'
7901
8530
  },
7902
8531
  brokerType: {
7903
- type: 'object',
8532
+ type: 'string',
7904
8533
  description: 'Broker type derived from Platform.type',
7905
8534
  nullable: true,
7906
8535
  example: 'broker'
@@ -7921,7 +8550,7 @@ export const $HoldingPnlRowDto = {
7921
8550
  example: 'EQUITY'
7922
8551
  },
7923
8552
  assetSubClass: {
7924
- type: 'object',
8553
+ type: 'string',
7925
8554
  nullable: true,
7926
8555
  example: 'STOCK'
7927
8556
  },
@@ -7968,14 +8597,14 @@ export const $HoldingPnlRowDto = {
7968
8597
  ]
7969
8598
  },
7970
8599
  unrealizedPnlBase: {
7971
- type: 'object',
8600
+ type: 'string',
7972
8601
  description:
7973
8602
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7974
8603
  nullable: true,
7975
8604
  example: '6000'
7976
8605
  },
7977
8606
  unrealizedPnlPct: {
7978
- type: 'object',
8607
+ type: 'string',
7979
8608
  description: 'Unrealized P&L % (Decimal string)',
7980
8609
  nullable: true,
7981
8610
  example: '25'
@@ -7999,7 +8628,7 @@ export const $HoldingPnlRowDto = {
7999
8628
  ]
8000
8629
  },
8001
8630
  pctOfInvestedAssets: {
8002
- type: 'object',
8631
+ type: 'string',
8003
8632
  description:
8004
8633
  'Share of invested assets % (Decimal string); only for invested chartTokens',
8005
8634
  nullable: true,
@@ -8044,15 +8673,15 @@ export const $HoldingPnlWarningDto = {
8044
8673
  ]
8045
8674
  },
8046
8675
  symbol: {
8047
- type: 'object',
8676
+ type: 'string',
8048
8677
  nullable: true
8049
8678
  },
8050
8679
  accountId: {
8051
- type: 'object',
8680
+ type: 'string',
8052
8681
  nullable: true
8053
8682
  },
8054
8683
  currency: {
8055
- type: 'object',
8684
+ type: 'string',
8056
8685
  nullable: true
8057
8686
  }
8058
8687
  },
@@ -8116,6 +8745,216 @@ export const $AnonymousLoginResponseDto = {
8116
8745
  required: ['authToken']
8117
8746
  } as const;
8118
8747
 
8748
+ export const $ParserContributionMetaDto = {
8749
+ type: 'object',
8750
+ properties: {
8751
+ institution: {
8752
+ type: 'string',
8753
+ description: 'Institution slug (lowercase kebab-case)',
8754
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8755
+ example: 'icbc'
8756
+ },
8757
+ region: {
8758
+ type: 'string',
8759
+ enum: [
8760
+ 'cn',
8761
+ 'us',
8762
+ 'de',
8763
+ 'fr',
8764
+ 'gb',
8765
+ 'hk',
8766
+ 'jp',
8767
+ 'sg',
8768
+ 'au',
8769
+ 'ca',
8770
+ 'other'
8771
+ ]
8772
+ },
8773
+ accountType: {
8774
+ type: 'string',
8775
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8776
+ },
8777
+ format: {
8778
+ type: 'string',
8779
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8780
+ },
8781
+ institutionDisplayName: {
8782
+ type: 'string',
8783
+ example: '中国工商银行'
8784
+ },
8785
+ encoding: {
8786
+ type: 'string',
8787
+ example: 'utf-8'
8788
+ },
8789
+ delimiter: {
8790
+ type: 'string',
8791
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8792
+ },
8793
+ headerRows: {
8794
+ type: 'number',
8795
+ default: 1,
8796
+ description: 'Header row count; the client omits the field when it is 1'
8797
+ },
8798
+ notes: {
8799
+ type: 'string',
8800
+ maxLength: 2000
8801
+ }
8802
+ },
8803
+ required: ['institution', 'region', 'accountType', 'format']
8804
+ } as const;
8805
+
8806
+ export const $ParserContributionSamplesDto = {
8807
+ type: 'object',
8808
+ properties: {
8809
+ rows: {
8810
+ description:
8811
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8812
+ type: 'array',
8813
+ items: {
8814
+ type: 'object'
8815
+ }
8816
+ },
8817
+ rawHeaders: {
8818
+ type: 'array',
8819
+ items: {
8820
+ type: 'string'
8821
+ }
8822
+ }
8823
+ },
8824
+ required: ['rows']
8825
+ } as const;
8826
+
8827
+ export const $FieldHintDto = {
8828
+ type: 'object',
8829
+ properties: {
8830
+ columnName: {
8831
+ type: 'string',
8832
+ example: '交易日期'
8833
+ },
8834
+ format: {
8835
+ type: 'string',
8836
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8837
+ example: 'yyyy-MM-dd'
8838
+ },
8839
+ signConvention: {
8840
+ type: 'string',
8841
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8842
+ },
8843
+ creditColumn: {
8844
+ type: 'string'
8845
+ },
8846
+ debitColumn: {
8847
+ type: 'string'
8848
+ }
8849
+ },
8850
+ required: ['columnName']
8851
+ } as const;
8852
+
8853
+ export const $ParserContributionFieldHintsDto = {
8854
+ type: 'object',
8855
+ properties: {
8856
+ date: {
8857
+ $ref: '#/components/schemas/FieldHintDto'
8858
+ },
8859
+ amount: {
8860
+ $ref: '#/components/schemas/FieldHintDto'
8861
+ },
8862
+ description: {
8863
+ $ref: '#/components/schemas/FieldHintDto'
8864
+ },
8865
+ balance: {
8866
+ $ref: '#/components/schemas/FieldHintDto'
8867
+ },
8868
+ payee: {
8869
+ $ref: '#/components/schemas/FieldHintDto'
8870
+ },
8871
+ reference: {
8872
+ $ref: '#/components/schemas/FieldHintDto'
8873
+ },
8874
+ category: {
8875
+ $ref: '#/components/schemas/FieldHintDto'
8876
+ }
8877
+ },
8878
+ required: ['date', 'amount']
8879
+ } as const;
8880
+
8881
+ export const $ExpectedTransactionDto = {
8882
+ type: 'object',
8883
+ properties: {
8884
+ date: {
8885
+ type: 'string',
8886
+ example: '2026-08-01'
8887
+ },
8888
+ amount: {
8889
+ type: 'number',
8890
+ example: -45.5
8891
+ },
8892
+ description: {
8893
+ type: 'string',
8894
+ example: '星巴克-***店'
8895
+ },
8896
+ payee: {
8897
+ type: 'string'
8898
+ },
8899
+ category: {
8900
+ type: 'string'
8901
+ }
8902
+ },
8903
+ required: ['date', 'amount', 'description']
8904
+ } as const;
8905
+
8906
+ export const $ParserContributionExamplesDto = {
8907
+ type: 'object',
8908
+ properties: {
8909
+ expectedTransactions: {
8910
+ type: 'array',
8911
+ items: {
8912
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8913
+ }
8914
+ }
8915
+ },
8916
+ required: ['expectedTransactions']
8917
+ } as const;
8918
+
8919
+ export const $ParserContributionRequestDto = {
8920
+ type: 'object',
8921
+ properties: {
8922
+ meta: {
8923
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8924
+ },
8925
+ samples: {
8926
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8927
+ },
8928
+ fieldHints: {
8929
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8930
+ },
8931
+ examples: {
8932
+ description: 'Omitted entirely by the client when empty',
8933
+ allOf: [
8934
+ {
8935
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8936
+ }
8937
+ ]
8938
+ }
8939
+ },
8940
+ required: ['meta', 'samples', 'fieldHints']
8941
+ } as const;
8942
+
8943
+ export const $ParserContributionRelayResponseDto = {
8944
+ type: 'object',
8945
+ properties: {
8946
+ issueUrl: {
8947
+ type: 'string',
8948
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8949
+ },
8950
+ issueNumber: {
8951
+ type: 'number',
8952
+ example: 42
8953
+ }
8954
+ },
8955
+ required: ['issueUrl', 'issueNumber']
8956
+ } as const;
8957
+
8119
8958
  export const $SymbolSearchResultDto = {
8120
8959
  type: 'object',
8121
8960
  properties: {
@@ -8124,35 +8963,35 @@ export const $SymbolSearchResultDto = {
8124
8963
  example: 'AAPL'
8125
8964
  },
8126
8965
  name: {
8127
- type: 'object',
8966
+ type: 'string',
8128
8967
  example: 'Apple Inc.',
8129
8968
  nullable: true
8130
8969
  },
8131
8970
  exchange: {
8132
- type: 'object',
8971
+ type: 'string',
8133
8972
  example: 'US',
8134
8973
  nullable: true
8135
8974
  },
8136
8975
  assetType: {
8137
- type: 'object',
8976
+ type: 'string',
8138
8977
  description: 'OpenBB asset_type (e.g. stock, etf)',
8139
8978
  example: 'stock',
8140
8979
  nullable: true
8141
8980
  },
8142
8981
  assetClass: {
8143
- type: 'object',
8982
+ type: 'string',
8144
8983
  description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8145
8984
  example: 'EQUITY',
8146
8985
  nullable: true
8147
8986
  },
8148
8987
  assetSubClass: {
8149
- type: 'object',
8988
+ type: 'string',
8150
8989
  description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8151
8990
  example: 'STOCK',
8152
8991
  nullable: true
8153
8992
  },
8154
8993
  currency: {
8155
- type: 'object',
8994
+ type: 'string',
8156
8995
  description: 'Trading currency (extra_data or inferred from exchange)',
8157
8996
  example: 'USD',
8158
8997
  nullable: true
@@ -8169,90 +9008,90 @@ export const $SymbolQuoteDto = {
8169
9008
  example: 'AAPL'
8170
9009
  },
8171
9010
  name: {
8172
- type: 'object',
9011
+ type: 'string',
8173
9012
  example: 'Apple Inc.',
8174
9013
  nullable: true
8175
9014
  },
8176
9015
  exchange: {
8177
- type: 'object',
9016
+ type: 'string',
8178
9017
  example: 'US',
8179
9018
  nullable: true
8180
9019
  },
8181
9020
  assetType: {
8182
- type: 'object',
9021
+ type: 'string',
8183
9022
  description: 'OpenBB asset_type',
8184
9023
  example: 'stock',
8185
9024
  nullable: true
8186
9025
  },
8187
9026
  assetClass: {
8188
- type: 'object',
9027
+ type: 'string',
8189
9028
  description: 'IGN asset class',
8190
9029
  example: 'EQUITY',
8191
9030
  nullable: true
8192
9031
  },
8193
9032
  assetSubClass: {
8194
- type: 'object',
9033
+ type: 'string',
8195
9034
  description: 'IGN asset sub-class',
8196
9035
  example: 'STOCK',
8197
9036
  nullable: true
8198
9037
  },
8199
9038
  currency: {
8200
- type: 'object',
9039
+ type: 'string',
8201
9040
  description: 'Trading currency (extra_data or inferred from exchange)',
8202
9041
  example: 'USD',
8203
9042
  nullable: true
8204
9043
  },
8205
9044
  price: {
8206
- type: 'object',
9045
+ type: 'string',
8207
9046
  description: 'Latest price (Decimal string)',
8208
9047
  example: '189.84',
8209
9048
  nullable: true
8210
9049
  },
8211
9050
  priceDate: {
8212
- type: 'object',
9051
+ type: 'string',
8213
9052
  description: 'Date the price was observed (ISO yyyy-MM-dd)',
8214
9053
  example: '2026-08-05',
8215
9054
  nullable: true
8216
9055
  },
8217
9056
  changePercent: {
8218
- type: 'object',
9057
+ type: 'number',
8219
9058
  description:
8220
9059
  '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.',
8221
9060
  example: 1.7,
8222
9061
  nullable: true
8223
9062
  },
8224
9063
  prevClose: {
8225
- type: 'object',
9064
+ type: 'string',
8226
9065
  description: 'Previous close (Decimal string)',
8227
9066
  nullable: true
8228
9067
  },
8229
9068
  open: {
8230
- type: 'object',
9069
+ type: 'string',
8231
9070
  description: 'Day open (Decimal string)',
8232
9071
  nullable: true
8233
9072
  },
8234
9073
  high: {
8235
- type: 'object',
9074
+ type: 'string',
8236
9075
  description: 'Day high (Decimal string)',
8237
9076
  nullable: true
8238
9077
  },
8239
9078
  low: {
8240
- type: 'object',
9079
+ type: 'string',
8241
9080
  description: 'Day low (Decimal string)',
8242
9081
  nullable: true
8243
9082
  },
8244
9083
  volume: {
8245
- type: 'object',
9084
+ type: 'string',
8246
9085
  description: 'Day volume (Decimal string)',
8247
9086
  nullable: true
8248
9087
  },
8249
9088
  yearHigh: {
8250
- type: 'object',
9089
+ type: 'string',
8251
9090
  description: '52-week high (Decimal string)',
8252
9091
  nullable: true
8253
9092
  },
8254
9093
  yearLow: {
8255
- type: 'object',
9094
+ type: 'string',
8256
9095
  description: '52-week low (Decimal string)',
8257
9096
  nullable: true
8258
9097
  }