@firela/api-types 0.0.0-canary.fb3ac1dd → 0.0.0-canary.fcd7d276

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -181,7 +189,8 @@ export const $AccountResponseDto = {
181
189
  },
182
190
  displayName: {
183
191
  type: 'string',
184
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
185
194
  example: 'Checking'
186
195
  },
187
196
  icon: {
@@ -197,7 +206,7 @@ export const $AccountResponseDto = {
197
206
  }
198
207
  },
199
208
  platformId: {
200
- type: 'object',
209
+ type: 'string',
201
210
  description: 'Platform ID (null if unbound)',
202
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
203
212
  },
@@ -277,6 +286,14 @@ export const $UpdateAccountDto = {
277
286
  description: 'Icon identifier',
278
287
  example: 'bank-custom'
279
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
280
297
  openDirectiveMeta: {
281
298
  type: 'object',
282
299
  description:
@@ -377,16 +394,43 @@ export const $AccountStandardResponseDto = {
377
394
  },
378
395
  name: {
379
396
  type: 'string',
380
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
381
399
  example: 'Housing Fund'
382
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
383
425
  description: {
384
426
  type: 'string',
385
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
386
429
  example: 'ICBC checking account for daily transactions'
387
430
  },
388
431
  tags: {
389
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
390
434
  example: ['bank', 'checking', 'primary'],
391
435
  type: 'array',
392
436
  items: {
@@ -498,7 +542,10 @@ export const $RegionConfigDto = {
498
542
  },
499
543
  locale: {
500
544
  type: 'string',
501
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
502
549
  }
503
550
  },
504
551
  required: ['currency', 'dateFormat', 'locale']
@@ -511,6 +558,12 @@ export const $RegionInfoDto = {
511
558
  type: 'string',
512
559
  example: 'de'
513
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
514
567
  displayName: {
515
568
  type: 'string',
516
569
  example: 'Germany'
@@ -529,7 +582,7 @@ export const $RegionInfoDto = {
529
582
  $ref: '#/components/schemas/RegionConfigDto'
530
583
  }
531
584
  },
532
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
533
586
  } as const;
534
587
 
535
588
  export const $RegionsMetadataResponseDto = {
@@ -776,7 +829,7 @@ export const $PostingResponseDto = {
776
829
  units: {
777
830
  type: 'string',
778
831
  description:
779
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
780
833
  example: '100.50'
781
834
  },
782
835
  currency: {
@@ -1142,7 +1195,7 @@ export const $PostingDetailDto = {
1142
1195
  units: {
1143
1196
  type: 'string',
1144
1197
  description:
1145
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1146
1199
  example: '100.50'
1147
1200
  },
1148
1201
  currency: {
@@ -1326,6 +1379,147 @@ export const $TransactionDetailDto = {
1326
1379
  ]
1327
1380
  } as const;
1328
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1329
1523
  export const $BalanceByCurrencyDto = {
1330
1524
  type: 'object',
1331
1525
  properties: {
@@ -1371,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1371
1565
  totalAmount: {
1372
1566
  type: 'string',
1373
1567
  description:
1374
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1568
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1375
1569
  example: '-6000.00'
1376
1570
  },
1377
1571
  currency: {
@@ -1397,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1397
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1398
1592
  } as const;
1399
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1400
1614
  export const $TransactionListResponseDto = {
1401
1615
  type: 'object',
1402
1616
  properties: {
@@ -1404,7 +1618,7 @@ export const $TransactionListResponseDto = {
1404
1618
  description: 'List of transactions',
1405
1619
  type: 'array',
1406
1620
  items: {
1407
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1408
1622
  }
1409
1623
  },
1410
1624
  total: {
@@ -1430,6 +1644,15 @@ export const $TransactionListResponseDto = {
1430
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1431
1645
  }
1432
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1433
1656
  }
1434
1657
  },
1435
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -1781,13 +2004,17 @@ export const $ReviewStatsDto = {
1781
2004
  type: 'object',
1782
2005
  description: 'Count by type'
1783
2006
  },
2007
+ resolved: {
2008
+ type: 'number',
2009
+ description: 'Current count of reviews in RESOLVED status'
2010
+ },
1784
2011
  oldestPending: {
1785
2012
  format: 'date-time',
1786
2013
  type: 'string',
1787
2014
  description: 'Oldest pending review date'
1788
2015
  }
1789
2016
  },
1790
- required: ['total', 'byType']
2017
+ required: ['total', 'byType', 'resolved']
1791
2018
  } as const;
1792
2019
 
1793
2020
  export const $DecisionOptionDto = {
@@ -2092,6 +2319,44 @@ export const $BatchResolveDto = {
2092
2319
  required: ['reviewIds', 'action']
2093
2320
  } as const;
2094
2321
 
2322
+ export const $BatchResolveItemDto = {
2323
+ type: 'object',
2324
+ properties: {
2325
+ reviewId: {
2326
+ type: 'string',
2327
+ description: 'Review item ID'
2328
+ },
2329
+ success: {
2330
+ type: 'boolean',
2331
+ description: 'Whether this item was resolved successfully'
2332
+ },
2333
+ resolutionId: {
2334
+ type: 'string',
2335
+ description:
2336
+ 'Resolution ID for undo. Present only on successful items (failed items stay PENDING).'
2337
+ },
2338
+ messageKey: {
2339
+ type: 'string',
2340
+ description:
2341
+ 'i18n message key for the per-item failure reason (e.g., review.payee.error.no_suggestion), mirroring the single-item resolve response. Present only on resolver rejections, not on unexpected errors.'
2342
+ },
2343
+ messageParams: {
2344
+ type: 'object',
2345
+ description:
2346
+ 'Parameters for message interpolation (e.g., { name: "PayeeName" })',
2347
+ additionalProperties: {
2348
+ type: 'string'
2349
+ }
2350
+ },
2351
+ error: {
2352
+ type: 'string',
2353
+ description:
2354
+ 'Diagnostic string: the messageKey on the resolver-rejection path; an i18n key mapped from the exception type on the unexpected-error path (#903, raw exception detail stays in server logs).'
2355
+ }
2356
+ },
2357
+ required: ['reviewId', 'success']
2358
+ } as const;
2359
+
2095
2360
  export const $BatchResolveResultDto = {
2096
2361
  type: 'object',
2097
2362
  properties: {
@@ -2107,7 +2372,7 @@ export const $BatchResolveResultDto = {
2107
2372
  description: 'Details for each item',
2108
2373
  type: 'array',
2109
2374
  items: {
2110
- type: 'string'
2375
+ $ref: '#/components/schemas/BatchResolveItemDto'
2111
2376
  }
2112
2377
  }
2113
2378
  },
@@ -2340,10 +2605,10 @@ export const $UpdatePayeeDto = {
2340
2605
  meta: {
2341
2606
  type: 'object',
2342
2607
  description:
2343
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2608
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2344
2609
  example: {
2345
2610
  location: 'Zhongguancun',
2346
- note: 'Updated note',
2611
+ note: 'Near subway station',
2347
2612
  favorite: true
2348
2613
  }
2349
2614
  },
@@ -2904,568 +3169,660 @@ export const $UpdateCommodityDto = {
2904
3169
  }
2905
3170
  } as const;
2906
3171
 
2907
- export const $CreateBeanPriceDto = {
3172
+ export const $CurrencyBalanceDto = {
2908
3173
  type: 'object',
2909
3174
  properties: {
2910
3175
  currency: {
2911
3176
  type: 'string',
2912
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2913
- example: 'USD'
2914
- },
2915
- quoteCurrency: {
2916
- type: 'string',
2917
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3177
+ description: 'ISO 4217 currency code',
2918
3178
  example: 'CNY'
2919
3179
  },
2920
- amount: {
2921
- type: 'number',
2922
- description:
2923
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2924
- example: 175.5,
2925
- minimum: 0
2926
- },
2927
- date: {
3180
+ balance: {
2928
3181
  type: 'string',
2929
- description: 'Price date (ISO 8601 format)',
2930
- example: '2024-11-05'
2931
- },
2932
- metadata: {
2933
- type: 'object',
2934
- description:
2935
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2936
- example: {
2937
- source: 'MANUAL',
2938
- note: 'Bank valuation report',
2939
- confidence: 0.95
2940
- }
3182
+ description: 'Balance amount',
3183
+ example: '500000.00'
2941
3184
  }
2942
3185
  },
2943
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3186
+ required: ['currency', 'balance']
2944
3187
  } as const;
2945
3188
 
2946
- export const $PriceResponseDto = {
3189
+ export const $TimeSeriesPointDto = {
2947
3190
  type: 'object',
2948
3191
  properties: {
2949
- id: {
3192
+ date: {
2950
3193
  type: 'string',
2951
- description: 'Unique identifier',
2952
- example: 'uuid-123-456'
3194
+ description: 'Date in YYYY-MM-DD format',
3195
+ example: '2024-06-15'
2953
3196
  },
2954
- userId: {
3197
+ value: {
2955
3198
  type: 'string',
2956
- description: 'User ID (owner of the price)',
2957
- example: 'user-123'
3199
+ description: 'Value at this date (in base currency)',
3200
+ example: '500000.00'
2958
3201
  },
2959
- currency: {
3202
+ change: {
2960
3203
  type: 'string',
2961
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2962
- example: 'BTC'
3204
+ description: 'Change from previous point',
3205
+ example: '5000.00'
2963
3206
  },
2964
- quoteCurrency: {
3207
+ assets: {
2965
3208
  type: 'string',
2966
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2967
- example: 'USD'
2968
- },
2969
- amount: {
2970
- type: 'number',
2971
- description:
2972
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2973
- example: 50000
3209
+ description: 'Total assets at this date (in base currency)',
3210
+ example: '494338.00'
2974
3211
  },
2975
- date: {
3212
+ liabilities: {
2976
3213
  type: 'string',
2977
- description:
2978
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2979
- example: '2024-01-01',
2980
- format: 'date'
3214
+ description: 'Total liabilities at this date (in base currency)',
3215
+ example: '310098.00'
2981
3216
  },
2982
- meta: {
2983
- type: 'object',
2984
- description:
2985
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2986
- example: {
2987
- source: 'MANUAL',
2988
- note: 'User-defined price',
2989
- confidence: 1
3217
+ byCurrency: {
3218
+ description: 'Multi-currency breakdown for this point',
3219
+ type: 'array',
3220
+ items: {
3221
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2990
3222
  }
3223
+ }
3224
+ },
3225
+ required: ['date', 'value']
3226
+ } as const;
3227
+
3228
+ export const $TrendSummaryDto = {
3229
+ type: 'object',
3230
+ properties: {
3231
+ startValue: {
3232
+ type: 'string',
3233
+ description: 'Value at start of period',
3234
+ example: '450000.00'
2991
3235
  },
2992
- createdAt: {
2993
- format: 'date-time',
3236
+ endValue: {
2994
3237
  type: 'string',
2995
- description: 'Creation timestamp',
2996
- example: '2024-11-03T10:00:00Z'
3238
+ description: 'Value at end of period',
3239
+ example: '500000.00'
2997
3240
  },
2998
- updatedAt: {
2999
- format: 'date-time',
3241
+ totalChange: {
3000
3242
  type: 'string',
3001
- description: 'Last update timestamp',
3002
- example: '2024-11-03T10:00:00Z'
3243
+ description: 'Total change over period',
3244
+ example: '50000.00'
3245
+ },
3246
+ totalChangePercentage: {
3247
+ type: 'string',
3248
+ description: 'Total change percentage',
3249
+ example: '+11.11%'
3003
3250
  }
3004
3251
  },
3005
- required: [
3006
- 'id',
3007
- 'userId',
3008
- 'currency',
3009
- 'quoteCurrency',
3010
- 'amount',
3011
- 'date',
3012
- 'meta',
3013
- 'createdAt',
3014
- 'updatedAt'
3015
- ]
3252
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3016
3253
  } as const;
3017
3254
 
3018
- export const $PriceListResponseDto = {
3255
+ export const $MultiCurrencyPointDto = {
3019
3256
  type: 'object',
3020
3257
  properties: {
3021
- items: {
3022
- description: 'List of prices',
3258
+ date: {
3259
+ type: 'string',
3260
+ description: 'Date in YYYY-MM-DD format',
3261
+ example: '2024-06-15'
3262
+ },
3263
+ byCurrency: {
3264
+ description: 'Balances by currency',
3023
3265
  type: 'array',
3024
3266
  items: {
3025
- $ref: '#/components/schemas/PriceResponseDto'
3267
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3026
3268
  }
3027
- },
3028
- total: {
3029
- type: 'number',
3030
- description: 'Total number of prices',
3031
- example: 42
3032
3269
  }
3033
3270
  },
3034
- required: ['items', 'total']
3271
+ required: ['date', 'byCurrency']
3035
3272
  } as const;
3036
3273
 
3037
- export const $UpdateBeanPriceDto = {
3274
+ export const $PortfolioTrendsResponseDto = {
3038
3275
  type: 'object',
3039
3276
  properties: {
3040
- currency: {
3041
- type: 'string',
3042
- description: 'Currency being priced'
3277
+ series: {
3278
+ description: 'Time series data points',
3279
+ type: 'array',
3280
+ items: {
3281
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3282
+ }
3043
3283
  },
3044
- quoteCurrency: {
3284
+ summary: {
3285
+ description: 'Period summary',
3286
+ allOf: [
3287
+ {
3288
+ $ref: '#/components/schemas/TrendSummaryDto'
3289
+ }
3290
+ ]
3291
+ },
3292
+ period: {
3045
3293
  type: 'string',
3046
- description: 'Quote currency (pricing currency)'
3294
+ description: 'Period requested',
3295
+ example: '6m'
3047
3296
  },
3048
- amount: {
3049
- type: 'number',
3050
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3051
- minimum: 0
3297
+ granularity: {
3298
+ type: 'string',
3299
+ description: 'Data granularity',
3300
+ example: 'month'
3052
3301
  },
3053
- date: {
3302
+ currency: {
3054
3303
  type: 'string',
3055
- description: 'Price date (ISO 8601 format)'
3304
+ description: 'Base currency for converted values',
3305
+ example: 'CNY'
3056
3306
  },
3057
- metadata: {
3058
- type: 'object',
3059
- description: 'Metadata'
3307
+ byCurrency: {
3308
+ description:
3309
+ 'Multi-currency time series (each point has currency breakdown)',
3310
+ type: 'array',
3311
+ items: {
3312
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3313
+ }
3314
+ },
3315
+ warnings: {
3316
+ description: 'Exchange rate warnings',
3317
+ type: 'array',
3318
+ items: {
3319
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3320
+ }
3060
3321
  }
3061
- }
3322
+ },
3323
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3062
3324
  } as const;
3063
3325
 
3064
- export const $CreateRecurringRuleDto = {
3326
+ export const $CashFlowPointDto = {
3065
3327
  type: 'object',
3066
3328
  properties: {
3067
- name: {
3329
+ month: {
3068
3330
  type: 'string',
3069
- description: 'Rule name (unique per user)',
3070
- maxLength: 100
3331
+ description: 'Month key (YYYY-MM)',
3332
+ example: '2024-03'
3071
3333
  },
3072
- icon: {
3334
+ income: {
3073
3335
  type: 'string',
3074
- description: 'Icon emoji',
3075
- maxLength: 10
3336
+ description: 'Income in base currency (absolute, converted)',
3337
+ example: '10000.00'
3076
3338
  },
3077
- frequency: {
3339
+ expense: {
3078
3340
  type: 'string',
3079
- description: 'Recurring frequency',
3080
- enum: [
3081
- 'WEEKLY',
3082
- 'BIWEEKLY',
3083
- 'MONTHLY',
3084
- 'BIMONTHLY',
3085
- 'QUARTERLY',
3086
- 'YEARLY',
3087
- 'CUSTOM'
3088
- ]
3089
- },
3090
- expectedAmount: {
3091
- type: 'number',
3092
- description: 'Expected amount (positive number)',
3093
- minimum: 0
3094
- },
3095
- expectedDay: {
3096
- type: 'number',
3097
- description: 'Expected day of month (1-31)',
3098
- minimum: 1,
3099
- maximum: 31
3341
+ description: 'Expense in base currency (absolute, converted)',
3342
+ example: '5000.00'
3100
3343
  },
3101
- customIntervalDays: {
3102
- type: 'number',
3103
- description: 'Custom interval in days (required for CUSTOM frequency)',
3104
- minimum: 1
3344
+ netSavings: {
3345
+ type: 'string',
3346
+ description: 'netSavings = income − expense (savings positive)',
3347
+ example: '5000.00'
3348
+ }
3349
+ },
3350
+ required: ['month', 'income', 'expense', 'netSavings']
3351
+ } as const;
3352
+
3353
+ export const $CashFlowTrendSummaryDto = {
3354
+ type: 'object',
3355
+ properties: {
3356
+ totalIncome: {
3357
+ type: 'string',
3358
+ description: 'Total income across the period',
3359
+ example: '60000.00'
3105
3360
  },
3106
- currency: {
3361
+ totalExpense: {
3107
3362
  type: 'string',
3108
- description: 'Currency code',
3109
- default: 'CNY',
3110
- maxLength: 10
3363
+ description: 'Total expense across the period',
3364
+ example: '30000.00'
3111
3365
  },
3112
- matchPayeePattern: {
3366
+ totalNetSavings: {
3113
3367
  type: 'string',
3114
- description: 'Payee matching pattern (supports wildcards)',
3115
- maxLength: 200
3368
+ description: 'income − expense across the period',
3369
+ example: '30000.00'
3116
3370
  },
3117
- matchAmountTolerance: {
3118
- type: 'number',
3119
- description: 'Amount tolerance percentage (0-1)',
3120
- default: 0.075,
3121
- minimum: 0,
3122
- maximum: 1
3123
- },
3124
- defaultExpenseAccount: {
3371
+ averageMonthlyNetSavings: {
3125
3372
  type: 'string',
3126
- description: 'Default expense account for auto-create',
3127
- maxLength: 200
3373
+ description:
3374
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3375
+ example: '5000.00'
3376
+ }
3377
+ },
3378
+ required: [
3379
+ 'totalIncome',
3380
+ 'totalExpense',
3381
+ 'totalNetSavings',
3382
+ 'averageMonthlyNetSavings'
3383
+ ]
3384
+ } as const;
3385
+
3386
+ export const $CashFlowTrendsResponseDto = {
3387
+ type: 'object',
3388
+ properties: {
3389
+ series: {
3390
+ description:
3391
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3392
+ type: 'array',
3393
+ items: {
3394
+ $ref: '#/components/schemas/CashFlowPointDto'
3395
+ }
3128
3396
  },
3129
- defaultPaymentAccount: {
3130
- type: 'string',
3131
- description: 'Default payment account for auto-create',
3132
- maxLength: 200
3397
+ summary: {
3398
+ description: 'Period totals',
3399
+ allOf: [
3400
+ {
3401
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3402
+ }
3403
+ ]
3133
3404
  },
3134
- defaultPayee: {
3405
+ period: {
3135
3406
  type: 'string',
3136
- description: 'Default payee for auto-create',
3137
- maxLength: 200
3138
- },
3139
- autoCreate: {
3140
- type: 'boolean',
3141
- description: 'Auto-create transaction when expected date arrives',
3142
- default: false
3407
+ description: 'Period requested',
3408
+ example: '6m'
3143
3409
  },
3144
- startDate: {
3410
+ granularity: {
3145
3411
  type: 'string',
3146
- description: 'Rule start date (ISO format)'
3412
+ description: 'Data granularity (v1 returns month buckets)',
3413
+ example: 'month'
3147
3414
  },
3148
- endDate: {
3415
+ currency: {
3149
3416
  type: 'string',
3150
- description: 'Rule end date (ISO format)'
3417
+ description: 'Base currency for converted values',
3418
+ example: 'CNY'
3419
+ },
3420
+ warnings: {
3421
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3422
+ type: 'array',
3423
+ items: {
3424
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3425
+ }
3151
3426
  }
3152
3427
  },
3153
- required: [
3154
- 'name',
3155
- 'frequency',
3156
- 'expectedAmount',
3157
- 'currency',
3158
- 'matchAmountTolerance',
3159
- 'autoCreate'
3160
- ]
3428
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3161
3429
  } as const;
3162
3430
 
3163
- export const $RecurringRuleResponseDto = {
3431
+ export const $GenerateSnapshotBody = {
3432
+ type: 'object',
3433
+ properties: {}
3434
+ } as const;
3435
+
3436
+ export const $GenerateSnapshotResponse = {
3437
+ type: 'object',
3438
+ properties: {}
3439
+ } as const;
3440
+
3441
+ export const $BackfillSnapshotsBody = {
3442
+ type: 'object',
3443
+ properties: {}
3444
+ } as const;
3445
+
3446
+ export const $BackfillSnapshotsResponse = {
3447
+ type: 'object',
3448
+ properties: {}
3449
+ } as const;
3450
+
3451
+ export const $CreateBeanPriceDto = {
3164
3452
  type: 'object',
3165
3453
  properties: {
3166
- id: {
3167
- type: 'string',
3168
- description: 'Rule ID'
3169
- },
3170
- userId: {
3454
+ currency: {
3171
3455
  type: 'string',
3172
- description: 'User ID'
3456
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3457
+ example: 'USD'
3173
3458
  },
3174
- name: {
3459
+ quoteCurrency: {
3175
3460
  type: 'string',
3176
- description: 'Rule name'
3461
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3462
+ example: 'CNY'
3177
3463
  },
3178
- icon: {
3179
- type: 'object',
3180
- description: 'Icon emoji'
3464
+ amount: {
3465
+ type: 'number',
3466
+ description:
3467
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3468
+ example: 175.5,
3469
+ minimum: 0
3181
3470
  },
3182
- frequency: {
3471
+ date: {
3183
3472
  type: 'string',
3184
- description: 'Recurring frequency'
3185
- },
3186
- expectedAmount: {
3187
- type: 'number',
3188
- description: 'Expected amount'
3473
+ description: 'Price date (ISO 8601 format)',
3474
+ example: '2024-11-05'
3189
3475
  },
3190
- expectedDay: {
3476
+ metadata: {
3191
3477
  type: 'object',
3192
- description: 'Expected day of month'
3478
+ description:
3479
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3480
+ example: {
3481
+ source: 'MANUAL',
3482
+ note: 'Bank valuation report',
3483
+ confidence: 0.95
3484
+ }
3485
+ }
3486
+ },
3487
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3488
+ } as const;
3489
+
3490
+ export const $PriceResponseDto = {
3491
+ type: 'object',
3492
+ properties: {
3493
+ id: {
3494
+ type: 'string',
3495
+ description: 'Unique identifier',
3496
+ example: 'uuid-123-456'
3193
3497
  },
3194
- customIntervalDays: {
3195
- type: 'object',
3196
- description: 'Custom interval in days'
3498
+ userId: {
3499
+ type: 'string',
3500
+ description: 'User ID (owner of the price)',
3501
+ example: 'user-123'
3197
3502
  },
3198
3503
  currency: {
3199
3504
  type: 'string',
3200
- description: 'Currency code'
3505
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3506
+ example: 'BTC'
3201
3507
  },
3202
- matchPayeePattern: {
3203
- type: 'object',
3204
- description: 'Payee matching pattern'
3508
+ quoteCurrency: {
3509
+ type: 'string',
3510
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3511
+ example: 'USD'
3205
3512
  },
3206
- matchAmountTolerance: {
3513
+ amount: {
3207
3514
  type: 'number',
3208
- description: 'Amount tolerance percentage'
3209
- },
3210
- defaultExpenseAccount: {
3211
- type: 'object',
3212
- description: 'Default expense account'
3213
- },
3214
- defaultPaymentAccount: {
3215
- type: 'object',
3216
- description: 'Default payment account'
3217
- },
3218
- defaultPayee: {
3219
- type: 'object',
3220
- description: 'Default payee'
3221
- },
3222
- isActive: {
3223
- type: 'boolean',
3224
- description: 'Whether rule is active'
3515
+ description:
3516
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3517
+ example: 50000
3225
3518
  },
3226
- startDate: {
3519
+ date: {
3227
3520
  type: 'string',
3228
- description: 'Rule start date (YYYY-MM-DD)'
3229
- },
3230
- endDate: {
3231
- type: 'object',
3232
- description: 'Rule end date (YYYY-MM-DD)'
3233
- },
3234
- autoCreate: {
3235
- type: 'boolean',
3236
- description: 'Auto-create transaction on expected date'
3521
+ description:
3522
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3523
+ example: '2024-01-01',
3524
+ format: 'date'
3237
3525
  },
3238
- lastOccurrence: {
3526
+ meta: {
3239
3527
  type: 'object',
3240
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3241
- },
3242
- totalCount: {
3243
- type: 'number',
3244
- description: 'Total matched transactions count'
3528
+ description:
3529
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3530
+ example: {
3531
+ source: 'MANUAL',
3532
+ note: 'User-defined price',
3533
+ confidence: 1
3534
+ }
3245
3535
  },
3246
3536
  createdAt: {
3247
3537
  format: 'date-time',
3248
3538
  type: 'string',
3249
- description: 'Created at timestamp'
3539
+ description: 'Creation timestamp',
3540
+ example: '2024-11-03T10:00:00Z'
3250
3541
  },
3251
3542
  updatedAt: {
3252
3543
  format: 'date-time',
3253
3544
  type: 'string',
3254
- description: 'Updated at timestamp'
3545
+ description: 'Last update timestamp',
3546
+ example: '2024-11-03T10:00:00Z'
3255
3547
  }
3256
3548
  },
3257
3549
  required: [
3258
3550
  'id',
3259
3551
  'userId',
3260
- 'name',
3261
- 'frequency',
3262
- 'expectedAmount',
3263
3552
  'currency',
3264
- 'matchAmountTolerance',
3265
- 'isActive',
3266
- 'startDate',
3267
- 'autoCreate',
3268
- 'totalCount',
3553
+ 'quoteCurrency',
3554
+ 'amount',
3555
+ 'date',
3556
+ 'meta',
3269
3557
  'createdAt',
3270
3558
  'updatedAt'
3271
3559
  ]
3272
3560
  } as const;
3273
3561
 
3274
- export const $CreateRuleFromTransactionDto = {
3562
+ export const $PriceListResponseDto = {
3275
3563
  type: 'object',
3276
3564
  properties: {
3277
- frequency: {
3278
- type: 'string',
3279
- description: 'Recurring frequency',
3280
- enum: [
3281
- 'WEEKLY',
3282
- 'BIWEEKLY',
3283
- 'MONTHLY',
3284
- 'BIMONTHLY',
3285
- 'QUARTERLY',
3286
- 'YEARLY',
3287
- 'CUSTOM'
3288
- ],
3289
- example: 'MONTHLY'
3290
- },
3291
- name: {
3565
+ items: {
3566
+ description: 'List of prices',
3567
+ type: 'array',
3568
+ items: {
3569
+ $ref: '#/components/schemas/PriceResponseDto'
3570
+ }
3571
+ },
3572
+ total: {
3573
+ type: 'number',
3574
+ description: 'Total number of prices',
3575
+ example: 42
3576
+ }
3577
+ },
3578
+ required: ['items', 'total']
3579
+ } as const;
3580
+
3581
+ export const $UpdateBeanPriceDto = {
3582
+ type: 'object',
3583
+ properties: {
3584
+ currency: {
3292
3585
  type: 'string',
3293
- description: 'Optional name override (default: transaction payee)',
3294
- maxLength: 100
3586
+ description: 'Currency being priced'
3295
3587
  },
3296
- icon: {
3588
+ quoteCurrency: {
3297
3589
  type: 'string',
3298
- description: 'Optional icon emoji',
3299
- maxLength: 10
3590
+ description: 'Quote currency (pricing currency)'
3591
+ },
3592
+ amount: {
3593
+ type: 'number',
3594
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3595
+ minimum: 0
3596
+ },
3597
+ date: {
3598
+ type: 'string',
3599
+ description: 'Price date (ISO 8601 format)'
3600
+ },
3601
+ metadata: {
3602
+ type: 'object',
3603
+ description: 'Metadata'
3604
+ }
3605
+ }
3606
+ } as const;
3607
+
3608
+ export const $DeleteOwnUserDto = {
3609
+ type: 'object',
3610
+ properties: {
3611
+ accessToken: {
3612
+ type: 'string',
3613
+ description: 'Access token for user verification',
3614
+ example: 'abc123xyz'
3300
3615
  }
3301
3616
  },
3302
- required: ['frequency']
3617
+ required: ['accessToken']
3303
3618
  } as const;
3304
3619
 
3305
- export const $RecurringRuleWithStatsResponseDto = {
3620
+ export const $UserSettingsResponseDto = {
3621
+ type: 'object',
3622
+ properties: {
3623
+ baseCurrency: {
3624
+ type: 'string',
3625
+ description:
3626
+ '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).',
3627
+ example: 'USD',
3628
+ nullable: true
3629
+ }
3630
+ },
3631
+ required: ['baseCurrency']
3632
+ } as const;
3633
+
3634
+ export const $UserResponseDto = {
3306
3635
  type: 'object',
3307
3636
  properties: {
3308
3637
  id: {
3309
3638
  type: 'string',
3310
- description: 'Rule ID'
3639
+ description: 'User ID'
3311
3640
  },
3312
- userId: {
3641
+ role: {
3313
3642
  type: 'string',
3314
- description: 'User ID'
3643
+ description: 'Assigned user role'
3315
3644
  },
3316
- name: {
3645
+ permissions: {
3646
+ description: 'Permission strings',
3647
+ type: 'array',
3648
+ items: {
3649
+ type: 'string'
3650
+ }
3651
+ },
3652
+ settings: {
3653
+ description: 'User settings',
3654
+ allOf: [
3655
+ {
3656
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3657
+ }
3658
+ ]
3659
+ }
3660
+ },
3661
+ required: ['id', 'role', 'permissions', 'settings']
3662
+ } as const;
3663
+
3664
+ export const $SignupDto = {
3665
+ type: 'object',
3666
+ properties: {
3667
+ turnstileToken: {
3317
3668
  type: 'string',
3318
- description: 'Rule name'
3669
+ description:
3670
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3671
+ example: '0.abc123def456...'
3672
+ }
3673
+ }
3674
+ } as const;
3675
+
3676
+ export const $SignupResponseDto = {
3677
+ type: 'object',
3678
+ properties: {
3679
+ authToken: {
3680
+ type: 'string',
3681
+ description: 'JWT auth token',
3682
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3319
3683
  },
3320
- icon: {
3321
- type: 'object',
3322
- description: 'Icon emoji'
3684
+ accessToken: {
3685
+ type: 'string',
3686
+ description: 'Auto-generated access token'
3323
3687
  },
3324
- frequency: {
3688
+ role: {
3325
3689
  type: 'string',
3326
- description: 'Recurring frequency'
3690
+ description: 'Assigned user role',
3691
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3692
+ }
3693
+ },
3694
+ required: ['authToken', 'accessToken', 'role']
3695
+ } as const;
3696
+
3697
+ export const $UpdateUserSettingDto = {
3698
+ type: 'object',
3699
+ properties: {
3700
+ secId: {
3701
+ type: 'number',
3702
+ description: 'Security ID'
3327
3703
  },
3328
- expectedAmount: {
3704
+ annualInterestRate: {
3329
3705
  type: 'number',
3330
- description: 'Expected amount'
3706
+ description: 'Annual interest rate',
3707
+ example: 0.05
3331
3708
  },
3332
- expectedDay: {
3333
- type: 'object',
3334
- description: 'Expected day of month'
3709
+ currency: {
3710
+ type: 'string',
3711
+ description: 'Currency code',
3712
+ example: 'USD'
3335
3713
  },
3336
- customIntervalDays: {
3337
- type: 'object',
3338
- description: 'Custom interval in days'
3714
+ baseCurrency: {
3715
+ type: 'string',
3716
+ description: 'Base currency code',
3717
+ example: 'USD'
3339
3718
  },
3340
- currency: {
3719
+ benchmark: {
3341
3720
  type: 'string',
3342
- description: 'Currency code'
3721
+ description: 'Benchmark symbol',
3722
+ example: 'SPY'
3343
3723
  },
3344
- matchPayeePattern: {
3345
- type: 'object',
3346
- description: 'Payee matching pattern'
3724
+ colorScheme: {
3725
+ type: 'string',
3726
+ description: 'Color scheme',
3727
+ enum: ['DARK', 'LIGHT']
3347
3728
  },
3348
- matchAmountTolerance: {
3349
- type: 'number',
3350
- description: 'Amount tolerance percentage'
3729
+ dateRange: {
3730
+ type: 'string',
3731
+ description: 'Date range filter',
3732
+ example: '1y'
3351
3733
  },
3352
- defaultExpenseAccount: {
3353
- type: 'object',
3354
- description: 'Default expense account'
3734
+ emergencyFund: {
3735
+ type: 'number',
3736
+ description: 'Emergency fund amount',
3737
+ example: 10000
3355
3738
  },
3356
- defaultPaymentAccount: {
3357
- type: 'object',
3358
- description: 'Default payment account'
3739
+ 'filters.accounts': {
3740
+ description: 'Account filter IDs',
3741
+ type: 'array',
3742
+ items: {
3743
+ type: 'string'
3744
+ }
3359
3745
  },
3360
- defaultPayee: {
3361
- type: 'object',
3362
- description: 'Default payee'
3746
+ 'filters.assetClasses': {
3747
+ description: 'Asset class filters',
3748
+ type: 'array',
3749
+ items: {
3750
+ type: 'string'
3751
+ }
3363
3752
  },
3364
- isActive: {
3365
- type: 'boolean',
3366
- description: 'Whether rule is active'
3753
+ 'filters.dataSource': {
3754
+ type: 'string',
3755
+ description: 'Data source filter'
3367
3756
  },
3368
- startDate: {
3757
+ 'filters.symbol': {
3369
3758
  type: 'string',
3370
- description: 'Rule start date (YYYY-MM-DD)'
3759
+ description: 'Symbol filter'
3371
3760
  },
3372
- endDate: {
3373
- type: 'object',
3374
- description: 'Rule end date (YYYY-MM-DD)'
3761
+ 'filters.tags': {
3762
+ description: 'Tag filters',
3763
+ type: 'array',
3764
+ items: {
3765
+ type: 'string'
3766
+ }
3375
3767
  },
3376
- autoCreate: {
3768
+ isExperimentalFeatures: {
3377
3769
  type: 'boolean',
3378
- description: 'Auto-create transaction on expected date'
3379
- },
3380
- lastOccurrence: {
3381
- type: 'object',
3382
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3770
+ description: 'Enable experimental features'
3383
3771
  },
3384
- totalCount: {
3385
- type: 'number',
3386
- description: 'Total matched transactions count'
3772
+ isRestrictedView: {
3773
+ type: 'boolean',
3774
+ description: 'Enable restricted view mode'
3387
3775
  },
3388
- createdAt: {
3389
- format: 'date-time',
3776
+ language: {
3390
3777
  type: 'string',
3391
- description: 'Created at timestamp'
3778
+ description: 'Language code',
3779
+ example: 'en'
3392
3780
  },
3393
- updatedAt: {
3394
- format: 'date-time',
3781
+ locale: {
3395
3782
  type: 'string',
3396
- description: 'Updated at timestamp'
3397
- },
3398
- pendingCount: {
3399
- type: 'number',
3400
- description: 'Number of pending expected transactions'
3783
+ description: 'Locale code',
3784
+ example: 'en-US'
3401
3785
  },
3402
- overdueCount: {
3786
+ projectedTotalAmount: {
3403
3787
  type: 'number',
3404
- description: 'Number of overdue expected transactions'
3788
+ description: 'Projected total amount',
3789
+ example: 1000000
3405
3790
  },
3406
- nextExpectedDate: {
3407
- type: 'object',
3408
- description: 'Next expected date (YYYY-MM-DD)'
3791
+ retirementDate: {
3792
+ type: 'string',
3793
+ description: 'Retirement date in ISO 8601 format',
3794
+ example: '2050-01-01'
3409
3795
  },
3410
- totalAmount: {
3796
+ savingsRate: {
3411
3797
  type: 'number',
3412
- description: 'Total amount of all matched transactions'
3798
+ description: 'Savings rate percentage',
3799
+ example: 0.2
3413
3800
  },
3414
- averageAmount: {
3415
- type: 'number',
3416
- description: 'Average amount per transaction'
3417
- },
3418
- transactionCount: {
3419
- type: 'number',
3420
- description: 'Number of matched transactions'
3421
- },
3422
- firstDate: {
3423
- type: 'object',
3424
- description: 'First matched transaction date (YYYY-MM-DD)'
3425
- },
3426
- lastDate: {
3427
- type: 'object',
3428
- description: 'Last matched transaction date (YYYY-MM-DD)'
3429
- },
3430
- variance: {
3431
- type: 'number',
3432
- description: 'Amount variance (standard deviation squared)'
3433
- },
3434
- upcomingCount: {
3435
- type: 'number',
3436
- description: 'Number of upcoming expected transactions'
3801
+ viewMode: {
3802
+ type: 'string',
3803
+ description: 'View mode',
3804
+ enum: ['DEFAULT', 'ZEN']
3805
+ }
3806
+ }
3807
+ } as const;
3808
+
3809
+ export const $UpdatePropertyDto = {
3810
+ type: 'object',
3811
+ properties: {
3812
+ value: {
3813
+ type: 'string',
3814
+ description: 'Property value'
3437
3815
  }
3438
3816
  },
3439
- required: [
3440
- 'id',
3441
- 'userId',
3442
- 'name',
3443
- 'frequency',
3444
- 'expectedAmount',
3445
- 'currency',
3446
- 'matchAmountTolerance',
3447
- 'isActive',
3448
- 'startDate',
3449
- 'autoCreate',
3450
- 'totalCount',
3451
- 'createdAt',
3452
- 'updatedAt',
3453
- 'pendingCount',
3454
- 'overdueCount',
3455
- 'totalAmount',
3456
- 'averageAmount',
3457
- 'transactionCount',
3458
- 'variance',
3459
- 'upcomingCount'
3460
- ]
3817
+ required: ['value']
3461
3818
  } as const;
3462
3819
 
3463
- export const $UpdateRecurringRuleDto = {
3820
+ export const $CreateRecurringRuleDto = {
3464
3821
  type: 'object',
3465
3822
  properties: {
3466
3823
  name: {
3467
3824
  type: 'string',
3468
- description: 'Rule name',
3825
+ description: 'Rule name (unique per user)',
3469
3826
  maxLength: 100
3470
3827
  },
3471
3828
  icon: {
@@ -3488,7 +3845,7 @@ export const $UpdateRecurringRuleDto = {
3488
3845
  },
3489
3846
  expectedAmount: {
3490
3847
  type: 'number',
3491
- description: 'Expected amount',
3848
+ description: 'Expected amount (positive number)',
3492
3849
  minimum: 0
3493
3850
  },
3494
3851
  expectedDay: {
@@ -3499,7 +3856,7 @@ export const $UpdateRecurringRuleDto = {
3499
3856
  },
3500
3857
  customIntervalDays: {
3501
3858
  type: 'number',
3502
- description: 'Custom interval in days',
3859
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3503
3860
  minimum: 1
3504
3861
  },
3505
3862
  currency: {
@@ -3509,118 +3866,136 @@ export const $UpdateRecurringRuleDto = {
3509
3866
  },
3510
3867
  matchPayeePattern: {
3511
3868
  type: 'string',
3512
- description: 'Payee matching pattern',
3869
+ description: 'Payee matching pattern (supports wildcards)',
3513
3870
  maxLength: 200
3514
3871
  },
3515
3872
  matchAmountTolerance: {
3516
3873
  type: 'number',
3517
3874
  description: 'Amount tolerance percentage (0-1)',
3875
+ default: 0.075,
3518
3876
  minimum: 0,
3519
3877
  maximum: 1
3520
3878
  },
3521
3879
  defaultExpenseAccount: {
3522
3880
  type: 'string',
3523
- description: 'Default expense account',
3881
+ description: 'Default expense account for auto-create',
3524
3882
  maxLength: 200
3525
3883
  },
3526
3884
  defaultPaymentAccount: {
3527
3885
  type: 'string',
3528
- description: 'Default payment account',
3886
+ description: 'Default payment account for auto-create',
3529
3887
  maxLength: 200
3530
3888
  },
3531
3889
  defaultPayee: {
3532
3890
  type: 'string',
3533
- description: 'Default payee',
3891
+ description: 'Default payee for auto-create',
3534
3892
  maxLength: 200
3535
3893
  },
3536
3894
  autoCreate: {
3537
3895
  type: 'boolean',
3538
- description: 'Auto-create transaction'
3896
+ description: 'Auto-create transaction when expected date arrives',
3897
+ default: false
3539
3898
  },
3540
- isActive: {
3541
- type: 'boolean',
3542
- description: 'Rule active status'
3899
+ startDate: {
3900
+ type: 'string',
3901
+ description: 'Rule start date (ISO format)'
3543
3902
  },
3544
3903
  endDate: {
3545
3904
  type: 'string',
3546
3905
  description: 'Rule end date (ISO format)'
3547
3906
  }
3548
- }
3907
+ },
3908
+ required: [
3909
+ 'name',
3910
+ 'frequency',
3911
+ 'expectedAmount',
3912
+ 'matchAmountTolerance',
3913
+ 'autoCreate'
3914
+ ]
3549
3915
  } as const;
3550
3916
 
3551
- export const $ExpectedTransactionRuleDto = {
3917
+ export const $RecurringRuleResponseDto = {
3552
3918
  type: 'object',
3553
3919
  properties: {
3920
+ id: {
3921
+ type: 'string',
3922
+ description: 'Rule ID'
3923
+ },
3924
+ userId: {
3925
+ type: 'string',
3926
+ description: 'User ID'
3927
+ },
3554
3928
  name: {
3555
3929
  type: 'string',
3556
3930
  description: 'Rule name'
3557
3931
  },
3558
3932
  icon: {
3559
- type: 'object',
3560
- description: 'Rule icon'
3933
+ type: 'string',
3934
+ description: 'Icon emoji'
3561
3935
  },
3562
3936
  frequency: {
3563
3937
  type: 'string',
3564
- description: 'Rule frequency'
3938
+ description: 'Recurring frequency'
3939
+ },
3940
+ expectedAmount: {
3941
+ type: 'number',
3942
+ description: 'Expected amount'
3943
+ },
3944
+ expectedDay: {
3945
+ type: 'number',
3946
+ description: 'Expected day of month'
3947
+ },
3948
+ customIntervalDays: {
3949
+ type: 'number',
3950
+ description: 'Custom interval in days'
3565
3951
  },
3566
3952
  currency: {
3567
3953
  type: 'string',
3568
3954
  description: 'Currency code'
3569
- }
3570
- },
3571
- required: ['name', 'frequency', 'currency']
3572
- } as const;
3573
-
3574
- export const $ExpectedTransactionResponseDto = {
3575
- type: 'object',
3576
- properties: {
3577
- id: {
3578
- type: 'string',
3579
- description: 'Expected transaction ID'
3580
3955
  },
3581
- userId: {
3956
+ matchPayeePattern: {
3582
3957
  type: 'string',
3583
- description: 'User ID'
3958
+ description: 'Payee matching pattern'
3584
3959
  },
3585
- ruleId: {
3586
- type: 'string',
3587
- description: 'Associated rule ID'
3960
+ matchAmountTolerance: {
3961
+ type: 'number',
3962
+ description: 'Amount tolerance percentage'
3588
3963
  },
3589
- expectedDate: {
3964
+ defaultExpenseAccount: {
3590
3965
  type: 'string',
3591
- description: 'Expected date (YYYY-MM-DD)'
3966
+ description: 'Default expense account'
3592
3967
  },
3593
- expectedAmount: {
3594
- type: 'number',
3595
- description: 'Expected amount'
3968
+ defaultPaymentAccount: {
3969
+ type: 'string',
3970
+ description: 'Default payment account'
3596
3971
  },
3597
- status: {
3972
+ defaultPayee: {
3598
3973
  type: 'string',
3599
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3974
+ description: 'Default payee'
3600
3975
  },
3601
- matchedTransactionId: {
3602
- type: 'object',
3603
- description: 'Matched transaction ID'
3976
+ isActive: {
3977
+ type: 'boolean',
3978
+ description: 'Whether rule is active'
3604
3979
  },
3605
- matchedAt: {
3606
- type: 'object',
3607
- description: 'Match timestamp (ISO 8601)'
3980
+ startDate: {
3981
+ type: 'string',
3982
+ description: 'Rule start date (YYYY-MM-DD)'
3608
3983
  },
3609
- matchConfidence: {
3610
- type: 'object',
3611
- description: 'Match confidence score (0-1)'
3984
+ endDate: {
3985
+ type: 'string',
3986
+ description: 'Rule end date (YYYY-MM-DD)'
3612
3987
  },
3613
- isOverdue: {
3988
+ autoCreate: {
3614
3989
  type: 'boolean',
3615
- description: 'Whether this expected transaction is overdue'
3990
+ description: 'Auto-create transaction on expected date'
3616
3991
  },
3617
- rule: {
3618
- description: 'Rule information',
3619
- allOf: [
3620
- {
3621
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3622
- }
3623
- ]
3992
+ lastOccurrence: {
3993
+ type: 'string',
3994
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3995
+ },
3996
+ totalCount: {
3997
+ type: 'number',
3998
+ description: 'Total matched transactions count'
3624
3999
  },
3625
4000
  createdAt: {
3626
4001
  format: 'date-time',
@@ -3636,647 +4011,581 @@ export const $ExpectedTransactionResponseDto = {
3636
4011
  required: [
3637
4012
  'id',
3638
4013
  'userId',
3639
- 'ruleId',
3640
- 'expectedDate',
4014
+ 'name',
4015
+ 'frequency',
3641
4016
  'expectedAmount',
3642
- 'status',
3643
- 'isOverdue',
3644
- 'rule',
4017
+ 'currency',
4018
+ 'matchAmountTolerance',
4019
+ 'isActive',
4020
+ 'startDate',
4021
+ 'autoCreate',
4022
+ 'totalCount',
3645
4023
  'createdAt',
3646
4024
  'updatedAt'
3647
4025
  ]
3648
4026
  } as const;
3649
4027
 
3650
- export const $ExpectedTransactionListResponseDto = {
4028
+ export const $CreateRuleFromTransactionDto = {
3651
4029
  type: 'object',
3652
4030
  properties: {
3653
- items: {
3654
- type: 'array',
3655
- items: {
3656
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3657
- }
4031
+ frequency: {
4032
+ type: 'string',
4033
+ description: 'Recurring frequency',
4034
+ enum: [
4035
+ 'WEEKLY',
4036
+ 'BIWEEKLY',
4037
+ 'MONTHLY',
4038
+ 'BIMONTHLY',
4039
+ 'QUARTERLY',
4040
+ 'YEARLY',
4041
+ 'CUSTOM'
4042
+ ],
4043
+ example: 'MONTHLY'
3658
4044
  },
3659
- total: {
3660
- type: 'number',
3661
- description: 'Total count'
3662
- }
3663
- },
3664
- required: ['items', 'total']
3665
- } as const;
3666
-
3667
- export const $ConfirmMatchDto = {
3668
- type: 'object',
3669
- properties: {
3670
- transactionId: {
4045
+ name: {
3671
4046
  type: 'string',
3672
- description: 'Transaction ID to match with'
4047
+ description: 'Optional name override (default: transaction payee)',
4048
+ maxLength: 100
4049
+ },
4050
+ icon: {
4051
+ type: 'string',
4052
+ description: 'Optional icon emoji',
4053
+ maxLength: 10
3673
4054
  }
3674
4055
  },
3675
- required: ['transactionId']
4056
+ required: ['frequency']
3676
4057
  } as const;
3677
4058
 
3678
- export const $EnterNowDto = {
4059
+ export const $RecurringRuleWithStatsResponseDto = {
3679
4060
  type: 'object',
3680
4061
  properties: {
3681
- expenseAccount: {
4062
+ id: {
3682
4063
  type: 'string',
3683
- description:
3684
- 'Override expense account (uses rule default if not provided)',
3685
- maxLength: 200
4064
+ description: 'Rule ID'
3686
4065
  },
3687
- paymentAccount: {
4066
+ userId: {
3688
4067
  type: 'string',
3689
- description:
3690
- 'Override payment account (uses rule default if not provided)',
3691
- maxLength: 200
4068
+ description: 'User ID'
3692
4069
  },
3693
- amount: {
3694
- type: 'number',
3695
- description: 'Override amount (uses expected amount if not provided)',
3696
- minimum: 0
4070
+ name: {
4071
+ type: 'string',
4072
+ description: 'Rule name'
3697
4073
  },
3698
- payee: {
4074
+ icon: {
3699
4075
  type: 'string',
3700
- description: 'Override payee (uses rule default if not provided)',
3701
- maxLength: 200
4076
+ description: 'Icon emoji'
3702
4077
  },
3703
- narration: {
4078
+ frequency: {
3704
4079
  type: 'string',
3705
- description: 'Optional narration',
3706
- maxLength: 500
3707
- }
3708
- }
3709
- } as const;
3710
-
3711
- export const $ForecastItemDto = {
3712
- type: 'object',
3713
- properties: {
3714
- rule: {
4080
+ description: 'Recurring frequency'
4081
+ },
4082
+ expectedAmount: {
4083
+ type: 'number',
4084
+ description: 'Expected amount'
4085
+ },
4086
+ expectedDay: {
4087
+ type: 'number',
4088
+ description: 'Expected day of month'
4089
+ },
4090
+ customIntervalDays: {
4091
+ type: 'number',
4092
+ description: 'Custom interval in days'
4093
+ },
4094
+ currency: {
3715
4095
  type: 'string',
3716
- description: 'Rule name',
3717
- example: 'Rent'
4096
+ description: 'Currency code'
3718
4097
  },
3719
- ruleId: {
4098
+ matchPayeePattern: {
3720
4099
  type: 'string',
3721
- description: 'Rule ID',
3722
- example: 'clx123...'
4100
+ description: 'Payee matching pattern'
3723
4101
  },
3724
- amount: {
4102
+ matchAmountTolerance: {
3725
4103
  type: 'number',
3726
- description: 'Expected amount',
3727
- example: 3000
4104
+ description: 'Amount tolerance percentage'
3728
4105
  },
3729
- date: {
4106
+ defaultExpenseAccount: {
3730
4107
  type: 'string',
3731
- description: 'Expected date (YYYY-MM-DD)',
3732
- example: '2024-04-01'
4108
+ description: 'Default expense account'
3733
4109
  },
3734
- icon: {
4110
+ defaultPaymentAccount: {
3735
4111
  type: 'string',
3736
- description: 'Rule icon emoji',
3737
- example: '🏠',
3738
- nullable: true
4112
+ description: 'Default payment account'
3739
4113
  },
3740
- currency: {
4114
+ defaultPayee: {
3741
4115
  type: 'string',
3742
- description: 'Currency code',
3743
- example: 'CNY'
3744
- }
3745
- },
3746
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3747
- } as const;
3748
-
3749
- export const $MonthlyForecastDto = {
3750
- type: 'object',
3751
- properties: {
3752
- month: {
4116
+ description: 'Default payee'
4117
+ },
4118
+ isActive: {
4119
+ type: 'boolean',
4120
+ description: 'Whether rule is active'
4121
+ },
4122
+ startDate: {
3753
4123
  type: 'string',
3754
- description: 'Month (YYYY-MM)',
3755
- example: '2024-04'
4124
+ description: 'Rule start date (YYYY-MM-DD)'
3756
4125
  },
3757
- expectedOutflow: {
4126
+ endDate: {
4127
+ type: 'string',
4128
+ description: 'Rule end date (YYYY-MM-DD)'
4129
+ },
4130
+ autoCreate: {
4131
+ type: 'boolean',
4132
+ description: 'Auto-create transaction on expected date'
4133
+ },
4134
+ lastOccurrence: {
4135
+ type: 'string',
4136
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4137
+ },
4138
+ totalCount: {
3758
4139
  type: 'number',
3759
- description: 'Total expected outflow for the month',
3760
- example: 8500
4140
+ description: 'Total matched transactions count'
3761
4141
  },
3762
- itemCount: {
4142
+ createdAt: {
4143
+ format: 'date-time',
4144
+ type: 'string',
4145
+ description: 'Created at timestamp'
4146
+ },
4147
+ updatedAt: {
4148
+ format: 'date-time',
4149
+ type: 'string',
4150
+ description: 'Updated at timestamp'
4151
+ },
4152
+ pendingCount: {
3763
4153
  type: 'number',
3764
- description: 'Number of expected transactions',
3765
- example: 3
4154
+ description: 'Number of pending expected transactions'
3766
4155
  },
3767
- byCurrency: {
3768
- type: 'object',
3769
- description: 'Breakdown by currency',
3770
- example: {
3771
- CNY: 8500,
3772
- USD: 100
3773
- }
4156
+ overdueCount: {
4157
+ type: 'number',
4158
+ description: 'Number of overdue expected transactions'
3774
4159
  },
3775
- items: {
3776
- description: 'Individual forecast items',
3777
- type: 'array',
3778
- items: {
3779
- $ref: '#/components/schemas/ForecastItemDto'
3780
- }
3781
- }
3782
- },
3783
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3784
- } as const;
3785
-
3786
- export const $ForecastResponseDto = {
3787
- type: 'object',
3788
- properties: {
3789
- forecast: {
3790
- description: 'Monthly forecast data',
3791
- type: 'array',
3792
- items: {
3793
- $ref: '#/components/schemas/MonthlyForecastDto'
3794
- }
4160
+ nextExpectedDate: {
4161
+ type: 'string',
4162
+ description: 'Next expected date (YYYY-MM-DD)'
3795
4163
  },
3796
- totalOutflow: {
4164
+ totalAmount: {
3797
4165
  type: 'number',
3798
- description: 'Total expected outflow across all months',
3799
- example: 25500
4166
+ description: 'Total amount of all matched transactions'
3800
4167
  },
3801
- totalByCurrency: {
3802
- type: 'object',
3803
- description: 'Total by currency across all months',
3804
- example: {
3805
- CNY: 25500,
3806
- USD: 300
3807
- }
4168
+ averageAmount: {
4169
+ type: 'number',
4170
+ description: 'Average amount per transaction'
3808
4171
  },
3809
- rulesCount: {
4172
+ transactionCount: {
3810
4173
  type: 'number',
3811
- description: 'Number of active recurring rules included',
3812
- example: 5
4174
+ description: 'Number of matched transactions'
3813
4175
  },
3814
- periodStart: {
4176
+ firstDate: {
3815
4177
  type: 'string',
3816
- description: 'Forecast period start date',
3817
- example: '2024-04-01'
4178
+ description: 'First matched transaction date (YYYY-MM-DD)'
3818
4179
  },
3819
- periodEnd: {
4180
+ lastDate: {
3820
4181
  type: 'string',
3821
- description: 'Forecast period end date',
3822
- example: '2024-06-30'
4182
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4183
+ },
4184
+ variance: {
4185
+ type: 'number',
4186
+ description: 'Amount variance (standard deviation squared)'
4187
+ },
4188
+ upcomingCount: {
4189
+ type: 'number',
4190
+ description: 'Number of upcoming expected transactions'
3823
4191
  }
3824
4192
  },
3825
4193
  required: [
3826
- 'forecast',
3827
- 'totalOutflow',
3828
- 'totalByCurrency',
3829
- 'rulesCount',
3830
- 'periodStart',
3831
- 'periodEnd'
4194
+ 'id',
4195
+ 'userId',
4196
+ 'name',
4197
+ 'frequency',
4198
+ 'expectedAmount',
4199
+ 'currency',
4200
+ 'matchAmountTolerance',
4201
+ 'isActive',
4202
+ 'startDate',
4203
+ 'autoCreate',
4204
+ 'totalCount',
4205
+ 'createdAt',
4206
+ 'updatedAt',
4207
+ 'pendingCount',
4208
+ 'overdueCount',
4209
+ 'totalAmount',
4210
+ 'averageAmount',
4211
+ 'transactionCount',
4212
+ 'variance',
4213
+ 'upcomingCount'
3832
4214
  ]
3833
4215
  } as const;
3834
4216
 
3835
- export const $CurrencyBalanceDto = {
4217
+ export const $UpdateRecurringRuleDto = {
3836
4218
  type: 'object',
3837
4219
  properties: {
3838
- currency: {
4220
+ name: {
3839
4221
  type: 'string',
3840
- description: 'ISO 4217 currency code',
3841
- example: 'CNY'
4222
+ description: 'Rule name (unique per user)',
4223
+ maxLength: 100
3842
4224
  },
3843
- balance: {
3844
- type: 'string',
3845
- description: 'Balance amount',
3846
- example: '500000.00'
3847
- }
3848
- },
3849
- required: ['currency', 'balance']
3850
- } as const;
3851
-
3852
- export const $TimeSeriesPointDto = {
3853
- type: 'object',
3854
- properties: {
3855
- date: {
4225
+ icon: {
3856
4226
  type: 'string',
3857
- description: 'Date in YYYY-MM-DD format',
3858
- example: '2024-06-15'
4227
+ description: 'Icon emoji',
4228
+ maxLength: 10
3859
4229
  },
3860
- value: {
4230
+ frequency: {
3861
4231
  type: 'string',
3862
- description: 'Value at this date (in base currency)',
3863
- example: '500000.00'
3864
- },
3865
- change: {
3866
- type: 'object',
3867
- description: 'Change from previous point',
3868
- example: '5000.00'
4232
+ description: 'Recurring frequency',
4233
+ enum: [
4234
+ 'WEEKLY',
4235
+ 'BIWEEKLY',
4236
+ 'MONTHLY',
4237
+ 'BIMONTHLY',
4238
+ 'QUARTERLY',
4239
+ 'YEARLY',
4240
+ 'CUSTOM'
4241
+ ]
3869
4242
  },
3870
- assets: {
3871
- type: 'string',
3872
- description: 'Total assets at this date (in base currency)',
3873
- example: '494338.00'
4243
+ expectedAmount: {
4244
+ type: 'number',
4245
+ description: 'Expected amount (positive number)',
4246
+ minimum: 0
3874
4247
  },
3875
- liabilities: {
3876
- type: 'string',
3877
- description: 'Total liabilities at this date (in base currency)',
3878
- example: '310098.00'
4248
+ expectedDay: {
4249
+ type: 'number',
4250
+ description: 'Expected day of month (1-31)',
4251
+ minimum: 1,
4252
+ maximum: 31
3879
4253
  },
3880
- byCurrency: {
3881
- description: 'Multi-currency breakdown for this point',
3882
- type: 'array',
3883
- items: {
3884
- $ref: '#/components/schemas/CurrencyBalanceDto'
3885
- }
3886
- }
3887
- },
3888
- required: ['date', 'value']
3889
- } as const;
3890
-
3891
- export const $TrendSummaryDto = {
3892
- type: 'object',
3893
- properties: {
3894
- startValue: {
4254
+ currency: {
3895
4255
  type: 'string',
3896
- description: 'Value at start of period',
3897
- example: '450000.00'
4256
+ description: 'Currency code',
4257
+ maxLength: 10
3898
4258
  },
3899
- endValue: {
4259
+ matchPayeePattern: {
3900
4260
  type: 'string',
3901
- description: 'Value at end of period',
3902
- example: '500000.00'
4261
+ description: 'Payee matching pattern (supports wildcards)',
4262
+ maxLength: 200
3903
4263
  },
3904
- totalChange: {
3905
- type: 'string',
3906
- description: 'Total change over period',
3907
- example: '50000.00'
4264
+ matchAmountTolerance: {
4265
+ type: 'number',
4266
+ description: 'Amount tolerance percentage (0-1)',
4267
+ default: 0.075,
4268
+ minimum: 0,
4269
+ maximum: 1
3908
4270
  },
3909
- totalChangePercentage: {
3910
- type: 'string',
3911
- description: 'Total change percentage',
3912
- example: '+11.11%'
3913
- }
3914
- },
3915
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3916
- } as const;
3917
-
3918
- export const $MultiCurrencyPointDto = {
3919
- type: 'object',
3920
- properties: {
3921
- date: {
4271
+ defaultExpenseAccount: {
3922
4272
  type: 'string',
3923
- description: 'Date in YYYY-MM-DD format',
3924
- example: '2024-06-15'
3925
- },
3926
- byCurrency: {
3927
- description: 'Balances by currency',
3928
- type: 'array',
3929
- items: {
3930
- $ref: '#/components/schemas/CurrencyBalanceDto'
3931
- }
3932
- }
3933
- },
3934
- required: ['date', 'byCurrency']
3935
- } as const;
3936
-
3937
- export const $PortfolioTrendsResponseDto = {
3938
- type: 'object',
3939
- properties: {
3940
- series: {
3941
- description: 'Time series data points',
3942
- type: 'array',
3943
- items: {
3944
- $ref: '#/components/schemas/TimeSeriesPointDto'
3945
- }
3946
- },
3947
- summary: {
3948
- description: 'Period summary',
3949
- allOf: [
3950
- {
3951
- $ref: '#/components/schemas/TrendSummaryDto'
3952
- }
3953
- ]
4273
+ description: 'Default expense account for auto-create',
4274
+ maxLength: 200
3954
4275
  },
3955
- period: {
4276
+ defaultPaymentAccount: {
3956
4277
  type: 'string',
3957
- description: 'Period requested',
3958
- example: '6m'
4278
+ description: 'Default payment account for auto-create',
4279
+ maxLength: 200
3959
4280
  },
3960
- granularity: {
4281
+ defaultPayee: {
3961
4282
  type: 'string',
3962
- description: 'Data granularity',
3963
- example: 'month'
4283
+ description: 'Default payee for auto-create',
4284
+ maxLength: 200
3964
4285
  },
3965
- currency: {
4286
+ autoCreate: {
4287
+ type: 'boolean',
4288
+ description: 'Auto-create transaction when expected date arrives',
4289
+ default: false
4290
+ },
4291
+ endDate: {
3966
4292
  type: 'string',
3967
- description: 'Base currency for converted values',
3968
- example: 'CNY'
4293
+ description: 'Rule end date (ISO format)'
3969
4294
  },
3970
- byCurrency: {
3971
- description:
3972
- 'Multi-currency time series (each point has currency breakdown)',
3973
- type: 'array',
3974
- items: {
3975
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3976
- }
4295
+ customIntervalDays: {
4296
+ type: 'number',
4297
+ description: 'Custom interval in days',
4298
+ minimum: 1
3977
4299
  },
3978
- warnings: {
3979
- description: 'Exchange rate warnings',
3980
- type: 'array',
3981
- items: {
3982
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3983
- }
4300
+ isActive: {
4301
+ type: 'boolean',
4302
+ description: 'Rule active status'
3984
4303
  }
3985
- },
3986
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4304
+ }
3987
4305
  } as const;
3988
4306
 
3989
- export const $CashFlowPointDto = {
4307
+ export const $ExpectedTransactionRuleDto = {
3990
4308
  type: 'object',
3991
4309
  properties: {
3992
- month: {
4310
+ name: {
3993
4311
  type: 'string',
3994
- description: 'Month key (YYYY-MM)',
3995
- example: '2024-03'
4312
+ description: 'Rule name'
3996
4313
  },
3997
- income: {
4314
+ icon: {
3998
4315
  type: 'string',
3999
- description: 'Income in base currency (absolute, converted)',
4000
- example: '10000.00'
4316
+ description: 'Rule icon'
4001
4317
  },
4002
- expense: {
4318
+ frequency: {
4003
4319
  type: 'string',
4004
- description: 'Expense in base currency (absolute, converted)',
4005
- example: '5000.00'
4320
+ description: 'Rule frequency'
4006
4321
  },
4007
- netSavings: {
4322
+ currency: {
4008
4323
  type: 'string',
4009
- description: 'netSavings = income − expense (savings positive)',
4010
- example: '5000.00'
4324
+ description: 'Currency code'
4011
4325
  }
4012
4326
  },
4013
- required: ['month', 'income', 'expense', 'netSavings']
4327
+ required: ['name', 'frequency', 'currency']
4014
4328
  } as const;
4015
4329
 
4016
- export const $CashFlowTrendSummaryDto = {
4330
+ export const $ExpectedTransactionResponseDto = {
4017
4331
  type: 'object',
4018
4332
  properties: {
4019
- totalIncome: {
4333
+ id: {
4020
4334
  type: 'string',
4021
- description: 'Total income across the period',
4022
- example: '60000.00'
4335
+ description: 'Expected transaction ID'
4023
4336
  },
4024
- totalExpense: {
4337
+ userId: {
4025
4338
  type: 'string',
4026
- description: 'Total expense across the period',
4027
- example: '30000.00'
4339
+ description: 'User ID'
4028
4340
  },
4029
- totalNetSavings: {
4341
+ ruleId: {
4030
4342
  type: 'string',
4031
- description: 'income − expense across the period',
4032
- example: '30000.00'
4343
+ description: 'Associated rule ID'
4033
4344
  },
4034
- averageMonthlyNetSavings: {
4345
+ expectedDate: {
4035
4346
  type: 'string',
4036
- description:
4037
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
4038
- example: '5000.00'
4039
- }
4040
- },
4041
- required: [
4042
- 'totalIncome',
4043
- 'totalExpense',
4044
- 'totalNetSavings',
4045
- 'averageMonthlyNetSavings'
4046
- ]
4047
- } as const;
4048
-
4049
- export const $CashFlowTrendsResponseDto = {
4050
- type: 'object',
4051
- properties: {
4052
- series: {
4053
- description:
4054
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4055
- type: 'array',
4056
- items: {
4057
- $ref: '#/components/schemas/CashFlowPointDto'
4058
- }
4347
+ description: 'Expected date (YYYY-MM-DD)'
4059
4348
  },
4060
- summary: {
4061
- description: 'Period totals',
4349
+ expectedAmount: {
4350
+ type: 'number',
4351
+ description: 'Expected amount'
4352
+ },
4353
+ status: {
4354
+ type: 'string',
4355
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4356
+ },
4357
+ matchedTransactionId: {
4358
+ type: 'string',
4359
+ description: 'Matched transaction ID'
4360
+ },
4361
+ matchedAt: {
4362
+ type: 'string',
4363
+ description: 'Match timestamp (ISO 8601)'
4364
+ },
4365
+ matchConfidence: {
4366
+ type: 'number',
4367
+ description: 'Match confidence score (0-1)'
4368
+ },
4369
+ isOverdue: {
4370
+ type: 'boolean',
4371
+ description: 'Whether this expected transaction is overdue'
4372
+ },
4373
+ rule: {
4374
+ description: 'Rule information',
4062
4375
  allOf: [
4063
4376
  {
4064
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4377
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4065
4378
  }
4066
4379
  ]
4067
4380
  },
4068
- period: {
4069
- type: 'string',
4070
- description: 'Period requested',
4071
- example: '6m'
4072
- },
4073
- granularity: {
4381
+ createdAt: {
4382
+ format: 'date-time',
4074
4383
  type: 'string',
4075
- description: 'Data granularity (v1 returns month buckets)',
4076
- example: 'month'
4384
+ description: 'Created at timestamp'
4077
4385
  },
4078
- currency: {
4386
+ updatedAt: {
4387
+ format: 'date-time',
4079
4388
  type: 'string',
4080
- description: 'Base currency for converted values',
4081
- example: 'CNY'
4082
- },
4083
- warnings: {
4084
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4085
- type: 'array',
4086
- items: {
4087
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4088
- }
4389
+ description: 'Updated at timestamp'
4089
4390
  }
4090
4391
  },
4091
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4092
- } as const;
4093
-
4094
- export const $GenerateSnapshotBody = {
4095
- type: 'object',
4096
- properties: {}
4097
- } as const;
4098
-
4099
- export const $GenerateSnapshotResponse = {
4100
- type: 'object',
4101
- properties: {}
4102
- } as const;
4103
-
4104
- export const $BackfillSnapshotsBody = {
4105
- type: 'object',
4106
- properties: {}
4107
- } as const;
4108
-
4109
- export const $BackfillSnapshotsResponse = {
4110
- type: 'object',
4111
- properties: {}
4392
+ required: [
4393
+ 'id',
4394
+ 'userId',
4395
+ 'ruleId',
4396
+ 'expectedDate',
4397
+ 'expectedAmount',
4398
+ 'status',
4399
+ 'isOverdue',
4400
+ 'rule',
4401
+ 'createdAt',
4402
+ 'updatedAt'
4403
+ ]
4112
4404
  } as const;
4113
4405
 
4114
- export const $DeleteOwnUserDto = {
4406
+ export const $ExpectedTransactionListResponseDto = {
4115
4407
  type: 'object',
4116
4408
  properties: {
4117
- accessToken: {
4118
- type: 'string',
4119
- description: 'Access token for user verification',
4120
- example: 'abc123xyz'
4409
+ items: {
4410
+ type: 'array',
4411
+ items: {
4412
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4413
+ }
4414
+ },
4415
+ total: {
4416
+ type: 'number',
4417
+ description: 'Total count'
4121
4418
  }
4122
4419
  },
4123
- required: ['accessToken']
4420
+ required: ['items', 'total']
4124
4421
  } as const;
4125
4422
 
4126
- export const $SignupDto = {
4423
+ export const $ConfirmMatchDto = {
4127
4424
  type: 'object',
4128
4425
  properties: {
4129
- turnstileToken: {
4426
+ transactionId: {
4130
4427
  type: 'string',
4131
- description:
4132
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4133
- example: '0.abc123def456...'
4428
+ description: 'Transaction ID to match with'
4134
4429
  }
4135
- }
4430
+ },
4431
+ required: ['transactionId']
4136
4432
  } as const;
4137
4433
 
4138
- export const $SignupResponseDto = {
4434
+ export const $EnterNowDto = {
4139
4435
  type: 'object',
4140
4436
  properties: {
4141
- authToken: {
4437
+ expenseAccount: {
4142
4438
  type: 'string',
4143
- description: 'JWT auth token',
4144
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4439
+ description:
4440
+ 'Override expense account (uses rule default if not provided)',
4441
+ maxLength: 200
4145
4442
  },
4146
- accessToken: {
4443
+ paymentAccount: {
4147
4444
  type: 'string',
4148
- description: 'Auto-generated access token'
4445
+ description:
4446
+ 'Override payment account (uses rule default if not provided)',
4447
+ maxLength: 200
4149
4448
  },
4150
- role: {
4449
+ amount: {
4450
+ type: 'number',
4451
+ description: 'Override amount (uses expected amount if not provided)',
4452
+ minimum: 0
4453
+ },
4454
+ payee: {
4455
+ type: 'string',
4456
+ description: 'Override payee (uses rule default if not provided)',
4457
+ maxLength: 200
4458
+ },
4459
+ narration: {
4151
4460
  type: 'string',
4152
- description: 'Assigned user role',
4153
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4461
+ description: 'Optional narration',
4462
+ maxLength: 500
4154
4463
  }
4155
- },
4156
- required: ['authToken', 'accessToken', 'role']
4464
+ }
4157
4465
  } as const;
4158
4466
 
4159
- export const $UpdateUserSettingDto = {
4467
+ export const $ForecastItemDto = {
4160
4468
  type: 'object',
4161
4469
  properties: {
4162
- secId: {
4163
- type: 'number',
4164
- description: 'Security ID'
4470
+ rule: {
4471
+ type: 'string',
4472
+ description: 'Rule name',
4473
+ example: 'Rent'
4165
4474
  },
4166
- annualInterestRate: {
4475
+ ruleId: {
4476
+ type: 'string',
4477
+ description: 'Rule ID',
4478
+ example: 'clx123...'
4479
+ },
4480
+ amount: {
4167
4481
  type: 'number',
4168
- description: 'Annual interest rate',
4169
- example: 0.05
4482
+ description: 'Expected amount',
4483
+ example: 3000
4170
4484
  },
4171
- currency: {
4485
+ date: {
4172
4486
  type: 'string',
4173
- description: 'Currency code',
4174
- example: 'USD'
4487
+ description: 'Expected date (YYYY-MM-DD)',
4488
+ example: '2024-04-01'
4175
4489
  },
4176
- baseCurrency: {
4490
+ icon: {
4177
4491
  type: 'string',
4178
- description: 'Base currency code',
4179
- example: 'USD'
4492
+ description: 'Rule icon emoji',
4493
+ example: '🏠',
4494
+ nullable: true
4180
4495
  },
4181
- benchmark: {
4496
+ currency: {
4182
4497
  type: 'string',
4183
- description: 'Benchmark symbol',
4184
- example: 'SPY'
4185
- },
4186
- colorScheme: {
4498
+ description: 'Currency code',
4499
+ example: 'CNY'
4500
+ }
4501
+ },
4502
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4503
+ } as const;
4504
+
4505
+ export const $MonthlyForecastDto = {
4506
+ type: 'object',
4507
+ properties: {
4508
+ month: {
4187
4509
  type: 'string',
4188
- description: 'Color scheme',
4189
- enum: ['DARK', 'LIGHT']
4510
+ description: 'Month (YYYY-MM)',
4511
+ example: '2024-04'
4190
4512
  },
4191
- dateRange: {
4192
- type: 'string',
4193
- description: 'Date range filter',
4194
- example: '1y'
4513
+ expectedOutflow: {
4514
+ type: 'number',
4515
+ description: 'Total expected outflow for the month',
4516
+ example: 8500
4195
4517
  },
4196
- emergencyFund: {
4518
+ itemCount: {
4197
4519
  type: 'number',
4198
- description: 'Emergency fund amount',
4199
- example: 10000
4520
+ description: 'Number of expected transactions',
4521
+ example: 3
4200
4522
  },
4201
- 'filters.accounts': {
4202
- description: 'Account filter IDs',
4203
- type: 'array',
4204
- items: {
4205
- type: 'string'
4523
+ byCurrency: {
4524
+ type: 'object',
4525
+ description: 'Breakdown by currency',
4526
+ example: {
4527
+ CNY: 8500,
4528
+ USD: 100
4206
4529
  }
4207
4530
  },
4208
- 'filters.assetClasses': {
4209
- description: 'Asset class filters',
4531
+ items: {
4532
+ description: 'Individual forecast items',
4210
4533
  type: 'array',
4211
4534
  items: {
4212
- type: 'string'
4535
+ $ref: '#/components/schemas/ForecastItemDto'
4213
4536
  }
4214
- },
4215
- 'filters.dataSource': {
4216
- type: 'string',
4217
- description: 'Data source filter'
4218
- },
4219
- 'filters.symbol': {
4220
- type: 'string',
4221
- description: 'Symbol filter'
4222
- },
4223
- 'filters.tags': {
4224
- description: 'Tag filters',
4537
+ }
4538
+ },
4539
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4540
+ } as const;
4541
+
4542
+ export const $ForecastResponseDto = {
4543
+ type: 'object',
4544
+ properties: {
4545
+ forecast: {
4546
+ description: 'Monthly forecast data',
4225
4547
  type: 'array',
4226
4548
  items: {
4227
- type: 'string'
4549
+ $ref: '#/components/schemas/MonthlyForecastDto'
4228
4550
  }
4229
4551
  },
4230
- isExperimentalFeatures: {
4231
- type: 'boolean',
4232
- description: 'Enable experimental features'
4233
- },
4234
- isRestrictedView: {
4235
- type: 'boolean',
4236
- description: 'Enable restricted view mode'
4237
- },
4238
- language: {
4239
- type: 'string',
4240
- description: 'Language code',
4241
- example: 'en'
4242
- },
4243
- locale: {
4244
- type: 'string',
4245
- description: 'Locale code',
4246
- example: 'en-US'
4247
- },
4248
- projectedTotalAmount: {
4552
+ totalOutflow: {
4249
4553
  type: 'number',
4250
- description: 'Projected total amount',
4251
- example: 1000000
4554
+ description: 'Total expected outflow across all months',
4555
+ example: 25500
4252
4556
  },
4253
- retirementDate: {
4254
- type: 'string',
4255
- description: 'Retirement date in ISO 8601 format',
4256
- example: '2050-01-01'
4557
+ totalByCurrency: {
4558
+ type: 'object',
4559
+ description: 'Total by currency across all months',
4560
+ example: {
4561
+ CNY: 25500,
4562
+ USD: 300
4563
+ }
4257
4564
  },
4258
- savingsRate: {
4565
+ rulesCount: {
4259
4566
  type: 'number',
4260
- description: 'Savings rate percentage',
4261
- example: 0.2
4567
+ description: 'Number of active recurring rules included',
4568
+ example: 5
4262
4569
  },
4263
- viewMode: {
4570
+ periodStart: {
4264
4571
  type: 'string',
4265
- description: 'View mode',
4266
- enum: ['DEFAULT', 'ZEN']
4267
- }
4268
- }
4269
- } as const;
4270
-
4271
- export const $UpdatePropertyDto = {
4272
- type: 'object',
4273
- properties: {
4274
- value: {
4572
+ description: 'Forecast period start date',
4573
+ example: '2024-04-01'
4574
+ },
4575
+ periodEnd: {
4275
4576
  type: 'string',
4276
- description: 'Property value'
4577
+ description: 'Forecast period end date',
4578
+ example: '2024-06-30'
4277
4579
  }
4278
4580
  },
4279
- required: ['value']
4581
+ required: [
4582
+ 'forecast',
4583
+ 'totalOutflow',
4584
+ 'totalByCurrency',
4585
+ 'rulesCount',
4586
+ 'periodStart',
4587
+ 'periodEnd'
4588
+ ]
4280
4589
  } as const;
4281
4590
 
4282
4591
  export const $CreateTransactionRuleDto = {
@@ -4838,7 +5147,8 @@ export const $UpdateTransactionRuleDto = {
4838
5147
  },
4839
5148
  matchLogic: {
4840
5149
  type: 'string',
4841
- enum: ['OR', 'AND']
5150
+ enum: ['OR', 'AND'],
5151
+ default: 'OR'
4842
5152
  },
4843
5153
  amountMin: {
4844
5154
  type: 'number',
@@ -4852,13 +5162,10 @@ export const $UpdateTransactionRuleDto = {
4852
5162
  },
4853
5163
  priority: {
4854
5164
  type: 'number',
5165
+ default: 50,
4855
5166
  minimum: 0,
4856
5167
  maximum: 1000
4857
5168
  },
4858
- enabled: {
4859
- type: 'boolean',
4860
- description: 'Enable or disable the rule'
4861
- },
4862
5169
  additionalTags: {
4863
5170
  items: {
4864
5171
  type: 'array'
@@ -4868,6 +5175,10 @@ export const $UpdateTransactionRuleDto = {
4868
5175
  },
4869
5176
  additionalMetadata: {
4870
5177
  type: 'object'
5178
+ },
5179
+ enabled: {
5180
+ type: 'boolean',
5181
+ description: 'Enable or disable the rule'
4871
5182
  }
4872
5183
  }
4873
5184
  } as const;
@@ -4896,36 +5207,113 @@ export const $TestRuleDto = {
4896
5207
  maxLength: 10
4897
5208
  }
4898
5209
  },
4899
- required: ['narration']
5210
+ required: ['narration']
5211
+ } as const;
5212
+
5213
+ export const $TestRuleResponseDto = {
5214
+ type: 'object',
5215
+ properties: {
5216
+ ruleId: {
5217
+ type: 'string',
5218
+ description: 'Rule ID that was tested'
5219
+ },
5220
+ matches: {
5221
+ type: 'boolean',
5222
+ description: 'Whether the rule matched the test data'
5223
+ },
5224
+ confidence: {
5225
+ type: 'number',
5226
+ description: 'Match confidence score (0-1)',
5227
+ example: 0.85
5228
+ },
5229
+ matchDetails: {
5230
+ type: 'object',
5231
+ description: 'Details of which fields matched',
5232
+ example: {
5233
+ narration: true,
5234
+ payee: false,
5235
+ categoryAccount: false
5236
+ }
5237
+ }
5238
+ },
5239
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5240
+ } as const;
5241
+
5242
+ export const $CategoryCatalogEntryDto = {
5243
+ type: 'object',
5244
+ properties: {
5245
+ slug: {
5246
+ type: 'string',
5247
+ description: 'Category slug (single source-of-truth)',
5248
+ example: 'food'
5249
+ },
5250
+ scenario: {
5251
+ type: 'string',
5252
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5253
+ enum: [
5254
+ 'expense',
5255
+ 'income',
5256
+ 'investment',
5257
+ 'banking',
5258
+ 'transfer',
5259
+ 'payment'
5260
+ ],
5261
+ example: 'expense'
5262
+ },
5263
+ icon: {
5264
+ type: 'string',
5265
+ description: 'Lucide icon name',
5266
+ example: 'utensils'
5267
+ },
5268
+ regions: {
5269
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5270
+ example: ['*'],
5271
+ type: 'array',
5272
+ items: {
5273
+ type: 'string'
5274
+ }
5275
+ },
5276
+ categoryAccounts: {
5277
+ description:
5278
+ "Beancount account paths (categoryAccount) of the region-enabled system rules whose categoryKeywords include this slug (#816). System rules only (public endpoint — user rules excluded); one-to-many by design (e.g. 'utilities' → Electricity/Water/Internet/Gas), sorted, [] when no rule maps the slug.",
5279
+ example: [
5280
+ 'Expenses:Utilities:Electricity',
5281
+ 'Expenses:Utilities:Gas',
5282
+ 'Expenses:Utilities:Internet',
5283
+ 'Expenses:Utilities:Water'
5284
+ ],
5285
+ type: 'array',
5286
+ items: {
5287
+ type: 'string'
5288
+ }
5289
+ }
5290
+ },
5291
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
4900
5292
  } as const;
4901
5293
 
4902
- export const $TestRuleResponseDto = {
5294
+ export const $CategoryCatalogListResponseDto = {
4903
5295
  type: 'object',
4904
5296
  properties: {
4905
- ruleId: {
4906
- type: 'string',
4907
- description: 'Rule ID that was tested'
4908
- },
4909
- matches: {
4910
- type: 'boolean',
4911
- description: 'Whether the rule matched the test data'
5297
+ items: {
5298
+ description: 'Category entries (region-scoped, query-filtered)',
5299
+ type: 'array',
5300
+ items: {
5301
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5302
+ }
4912
5303
  },
4913
- confidence: {
5304
+ total: {
4914
5305
  type: 'number',
4915
- description: 'Match confidence score (0-1)',
4916
- example: 0.85
5306
+ description:
5307
+ 'Total category entries for the region (before query filtering)',
5308
+ example: 30
4917
5309
  },
4918
- matchDetails: {
4919
- type: 'object',
4920
- description: 'Details of which fields matched',
4921
- example: {
4922
- narration: true,
4923
- payee: false,
4924
- categoryAccount: false
4925
- }
5310
+ region: {
5311
+ type: 'string',
5312
+ description: 'Region code',
5313
+ example: 'cn'
4926
5314
  }
4927
5315
  },
4928
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5316
+ required: ['items', 'total', 'region']
4929
5317
  } as const;
4930
5318
 
4931
5319
  export const $CreateBeanEventDto = {
@@ -5091,6 +5479,14 @@ export const $OnboardingAccountDto = {
5091
5479
  description:
5092
5480
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5093
5481
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5482
+ },
5483
+ displayName: {
5484
+ type: 'string',
5485
+ description:
5486
+ 'User-set display name override (omit/null = keep the derived name)',
5487
+ nullable: true,
5488
+ maxLength: 50,
5489
+ example: 'Salary card'
5094
5490
  }
5095
5491
  },
5096
5492
  required: ['path', 'currency']
@@ -5670,7 +6066,8 @@ export const $UpdateMapperDefaultsDto = {
5670
6066
  type: 'string',
5671
6067
  description: 'Source account for transactions (Beancount format)',
5672
6068
  example: 'Assets:CN:Alipay:Balance',
5673
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6069
+ pattern:
6070
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5674
6071
  },
5675
6072
  currency: {
5676
6073
  type: 'string',
@@ -5684,13 +6081,15 @@ export const $UpdateMapperDefaultsDto = {
5684
6081
  type: 'string',
5685
6082
  description: 'Default expense account (optional)',
5686
6083
  example: 'Expenses:Unknown',
5687
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6084
+ pattern:
6085
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5688
6086
  },
5689
6087
  incomeAccount: {
5690
6088
  type: 'string',
5691
6089
  description: 'Default income account (optional)',
5692
6090
  example: 'Income:Unknown',
5693
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6091
+ pattern:
6092
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5694
6093
  },
5695
6094
  methodAccountMapping: {
5696
6095
  type: 'object',
@@ -5747,12 +6146,14 @@ export const $ProviderSyncConfigDto = {
5747
6146
  },
5748
6147
  defaultExpenseAccount: {
5749
6148
  type: 'string',
5750
- description: 'Default expense account for the second posting',
6149
+ description:
6150
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5751
6151
  example: 'Expenses:Unknown'
5752
6152
  },
5753
6153
  defaultIncomeAccount: {
5754
6154
  type: 'string',
5755
- description: 'Default income account for the second posting',
6155
+ description:
6156
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5756
6157
  example: 'Income:Unknown'
5757
6158
  },
5758
6159
  filterPending: {
@@ -5767,12 +6168,7 @@ export const $ProviderSyncConfigDto = {
5767
6168
  example: 'acc_gocardless_001'
5768
6169
  }
5769
6170
  },
5770
- required: [
5771
- 'sourceAccount',
5772
- 'defaultCurrency',
5773
- 'defaultExpenseAccount',
5774
- 'defaultIncomeAccount'
5775
- ]
6171
+ required: ['sourceAccount', 'defaultCurrency']
5776
6172
  } as const;
5777
6173
 
5778
6174
  export const $ProviderSyncDto = {
@@ -5982,15 +6378,106 @@ export const $UncoveredFormatMissDto = {
5982
6378
  properties: {}
5983
6379
  } as const;
5984
6380
 
6381
+ export const $ClientParsedDataDto = {
6382
+ type: 'object',
6383
+ properties: {
6384
+ amount: {
6385
+ type: 'number',
6386
+ description: 'Transaction amount',
6387
+ example: 35
6388
+ },
6389
+ currency: {
6390
+ type: 'string',
6391
+ description: 'Currency code',
6392
+ example: 'CNY'
6393
+ },
6394
+ date: {
6395
+ type: 'string',
6396
+ description: 'Transaction date (ISO 8601)',
6397
+ example: '2026-08-15'
6398
+ },
6399
+ payee: {
6400
+ type: 'string',
6401
+ description: 'Payee/merchant name',
6402
+ example: 'Starbucks'
6403
+ },
6404
+ narration: {
6405
+ type: 'string',
6406
+ description: 'Transaction narration'
6407
+ },
6408
+ category: {
6409
+ type: 'string',
6410
+ description:
6411
+ 'Category in canonical form (catalog slug or Stage-2 rule keyword token, ADR-0116) — echo back verbatim from parsedData.category. Foreign forms (locale display names, account paths) are rejected with nlp.category.invalid.',
6412
+ example: 'food'
6413
+ },
6414
+ incomeType: {
6415
+ type: 'string',
6416
+ description: 'Income type',
6417
+ example: 'Salary'
6418
+ },
6419
+ incomeSource: {
6420
+ type: 'string',
6421
+ description: 'Income source',
6422
+ example: 'Anthropic Inc.'
6423
+ },
6424
+ symbol: {
6425
+ type: 'string',
6426
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6427
+ example: 'AAPL'
6428
+ },
6429
+ quantity: {
6430
+ type: 'number',
6431
+ description: 'Quantity of shares/units',
6432
+ example: 100
6433
+ },
6434
+ price: {
6435
+ type: 'number',
6436
+ description: 'Unit price per share/unit',
6437
+ example: 1900
6438
+ },
6439
+ investmentAction: {
6440
+ type: 'string',
6441
+ description: 'Investment action',
6442
+ enum: ['buy', 'sell'],
6443
+ example: 'buy'
6444
+ },
6445
+ paymentSource: {
6446
+ type: 'string',
6447
+ description: 'Payment source: asset (default) or liability (credit card)',
6448
+ enum: ['asset', 'liability'],
6449
+ example: 'asset'
6450
+ },
6451
+ liabilityHint: {
6452
+ type: 'string',
6453
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6454
+ example: 'CreditCard'
6455
+ },
6456
+ warning: {
6457
+ type: 'string',
6458
+ description:
6459
+ 'Display-only warning from the prior response; accepted but ignored.',
6460
+ example: 'Cross-currency settlement applies.'
6461
+ }
6462
+ }
6463
+ } as const;
6464
+
5985
6465
  export const $ProcessNlpDto = {
5986
6466
  type: 'object',
5987
6467
  properties: {
5988
6468
  message: {
5989
6469
  type: 'string',
5990
- description: 'Natural language text describing a transaction (Chinese)',
5991
- example: 'yesterday Starbucks spent 35 yuan',
6470
+ description:
6471
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6472
+ example: 'Starbucks 35',
5992
6473
  maxLength: 500
5993
6474
  },
6475
+ confirm: {
6476
+ type: 'boolean',
6477
+ description:
6478
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6479
+ example: true
6480
+ },
5994
6481
  sessionId: {
5995
6482
  type: 'string',
5996
6483
  description:
@@ -5998,14 +6485,18 @@ export const $ProcessNlpDto = {
5998
6485
  example: 'session_abc123'
5999
6486
  },
6000
6487
  parsedData: {
6001
- type: 'object',
6002
6488
  description:
6003
6489
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
6004
6490
  example: {
6005
6491
  amount: 35,
6006
6492
  currency: 'CNY',
6007
6493
  payee: 'Starbucks'
6008
- }
6494
+ },
6495
+ allOf: [
6496
+ {
6497
+ $ref: '#/components/schemas/ClientParsedDataDto'
6498
+ }
6499
+ ]
6009
6500
  },
6010
6501
  selectedRuleId: {
6011
6502
  type: 'string',
@@ -6016,11 +6507,29 @@ export const $ProcessNlpDto = {
6016
6507
  selectedAccount: {
6017
6508
  type: 'string',
6018
6509
  description:
6019
- '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.',
6510
+ '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.',
6020
6511
  example: 'Expenses:Food:Coffee'
6512
+ },
6513
+ viewpointAccount: {
6514
+ type: 'string',
6515
+ description:
6516
+ '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.',
6517
+ example: 'Assets:CN:Bank:ICBC'
6518
+ },
6519
+ viewpointCategory: {
6520
+ type: 'string',
6521
+ description:
6522
+ "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.",
6523
+ example: 'Food'
6524
+ },
6525
+ viewpointFlow: {
6526
+ type: 'string',
6527
+ description:
6528
+ "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.",
6529
+ enum: ['income', 'expense'],
6530
+ example: 'expense'
6021
6531
  }
6022
- },
6023
- required: ['message']
6532
+ }
6024
6533
  } as const;
6025
6534
 
6026
6535
  export const $NlpTransactionInfoDto = {
@@ -6086,7 +6595,9 @@ export const $NlpParsedDataDto = {
6086
6595
  },
6087
6596
  category: {
6088
6597
  type: 'string',
6089
- description: 'Category'
6598
+ description:
6599
+ 'Category in canonical form: a CATEGORY_CATALOG slug (GET /{region}/bean/categories) or a Stage-2 rule categoryKeywords token (ADR-0116 D2). Never a locale display name or a beancount account path. Echo back verbatim on confirm.',
6600
+ example: 'food'
6090
6601
  },
6091
6602
  incomeType: {
6092
6603
  type: 'string',
@@ -6332,6 +6843,24 @@ export const $NlpRuleConfirmationDataDto = {
6332
6843
  ]
6333
6844
  } as const;
6334
6845
 
6846
+ export const $NlpAccountCandidateDto = {
6847
+ type: 'object',
6848
+ properties: {
6849
+ path: {
6850
+ type: 'string',
6851
+ description: 'Canonical beancount account path (echo back on selection)',
6852
+ example: 'Expenses:Food:Dining'
6853
+ },
6854
+ name: {
6855
+ type: 'string',
6856
+ description:
6857
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6858
+ example: '餐饮'
6859
+ }
6860
+ },
6861
+ required: ['path', 'name']
6862
+ } as const;
6863
+
6335
6864
  export const $NlpAccountConfirmationDataDto = {
6336
6865
  type: 'object',
6337
6866
  properties: {
@@ -6347,10 +6876,11 @@ export const $NlpAccountConfirmationDataDto = {
6347
6876
  example: 'Expenses:Food:Drinks'
6348
6877
  },
6349
6878
  similarAccounts: {
6350
- description: 'Similar accounts for user selection',
6879
+ description:
6880
+ 'Similar accounts for user selection (path + localized name, #680)',
6351
6881
  type: 'array',
6352
6882
  items: {
6353
- type: 'string'
6883
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6354
6884
  }
6355
6885
  },
6356
6886
  errorMessage: {
@@ -6638,7 +7168,7 @@ export const $NlpResponseDto = {
6638
7168
  type: 'string',
6639
7169
  description:
6640
7170
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6641
- enum: ['transfer', 'banking', 'investment'],
7171
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6642
7172
  example: 'investment'
6643
7173
  },
6644
7174
  liabilitySubType: {
@@ -6841,13 +7371,26 @@ export const $PlatformListItemDto = {
6841
7371
  suggestedSegment: {
6842
7372
  type: 'string',
6843
7373
  description:
6844
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7374
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6845
7375
  },
6846
7376
  logoUrl: {
6847
7377
  type: 'string',
6848
7378
  description: 'Logo URL',
6849
7379
  nullable: true
6850
7380
  },
7381
+ countryCode: {
7382
+ type: 'string',
7383
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7384
+ example: 'CN',
7385
+ nullable: true
7386
+ },
7387
+ category: {
7388
+ type: 'string',
7389
+ description:
7390
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7391
+ nullable: true,
7392
+ example: 'DigitalWallet'
7393
+ },
6851
7394
  isBound: {
6852
7395
  type: 'boolean',
6853
7396
  description: 'Whether user has accounts using this platform'
@@ -6861,6 +7404,8 @@ export const $PlatformListItemDto = {
6861
7404
  'canonical',
6862
7405
  'suggestedSegment',
6863
7406
  'logoUrl',
7407
+ 'countryCode',
7408
+ 'category',
6864
7409
  'isBound'
6865
7410
  ]
6866
7411
  } as const;
@@ -6878,7 +7423,110 @@ export const $PlatformMatchResultDto = {
6878
7423
  },
6879
7424
  canonical: {
6880
7425
  type: 'string',
6881
- description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7426
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7427
+ },
7428
+ type: {
7429
+ type: 'string',
7430
+ description: 'Platform type',
7431
+ enum: [
7432
+ 'BANK',
7433
+ 'BROKERAGE',
7434
+ 'CRYPTO_EXCHANGE',
7435
+ 'PAYMENT',
7436
+ 'INVESTMENT',
7437
+ 'INSURANCE',
7438
+ 'OTHER'
7439
+ ]
7440
+ },
7441
+ suggestedSegment: {
7442
+ type: 'string',
7443
+ description:
7444
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7445
+ },
7446
+ logoUrl: {
7447
+ type: 'string',
7448
+ description: 'Logo URL',
7449
+ nullable: true
7450
+ },
7451
+ countryCode: {
7452
+ type: 'string',
7453
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7454
+ example: 'CN',
7455
+ nullable: true
7456
+ },
7457
+ category: {
7458
+ type: 'string',
7459
+ description:
7460
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7461
+ nullable: true,
7462
+ example: 'DigitalWallet'
7463
+ },
7464
+ matchType: {
7465
+ type: 'string',
7466
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7467
+ enum: ['exact', 'prefix', 'substring']
7468
+ }
7469
+ },
7470
+ required: [
7471
+ 'id',
7472
+ 'name',
7473
+ 'canonical',
7474
+ 'type',
7475
+ 'suggestedSegment',
7476
+ 'logoUrl',
7477
+ 'countryCode',
7478
+ 'category',
7479
+ 'matchType'
7480
+ ]
7481
+ } as const;
7482
+
7483
+ export const $PlatformMatchResponseDto = {
7484
+ type: 'object',
7485
+ properties: {
7486
+ platforms: {
7487
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7488
+ type: 'array',
7489
+ items: {
7490
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7491
+ }
7492
+ },
7493
+ matchType: {
7494
+ type: 'string',
7495
+ description:
7496
+ "Overall match quality — top row's tier, or 'none' when no hits",
7497
+ enum: ['none', 'exact', 'prefix', 'substring']
7498
+ },
7499
+ total: {
7500
+ type: 'number',
7501
+ description: 'Total matches before LIMIT (truncation transparency)'
7502
+ },
7503
+ hasMore: {
7504
+ type: 'boolean',
7505
+ description: 'true when total > platforms.length (more matches exist)'
7506
+ }
7507
+ },
7508
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7509
+ } as const;
7510
+
7511
+ export const $PlatformStandardsPlatformDto = {
7512
+ type: 'object',
7513
+ properties: {
7514
+ id: {
7515
+ type: 'string',
7516
+ description: 'Global platform ID'
7517
+ },
7518
+ name: {
7519
+ type: 'string',
7520
+ description: 'Platform name (e.g., "ICBC")'
7521
+ },
7522
+ canonical: {
7523
+ type: 'string',
7524
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7525
+ },
7526
+ suggestedSegment: {
7527
+ type: 'string',
7528
+ description:
7529
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6882
7530
  },
6883
7531
  type: {
6884
7532
  type: 'string',
@@ -6893,59 +7541,44 @@ export const $PlatformMatchResultDto = {
6893
7541
  'OTHER'
6894
7542
  ]
6895
7543
  },
6896
- suggestedSegment: {
7544
+ category: {
6897
7545
  type: 'string',
6898
7546
  description:
6899
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
6900
- },
6901
- logoUrl: {
6902
- type: 'string',
6903
- description: 'Logo URL',
6904
- nullable: true
6905
- },
6906
- matchType: {
6907
- type: 'string',
6908
- description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6909
- enum: ['exact', 'prefix', 'substring']
7547
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7548
+ nullable: true,
7549
+ example: 'Bank'
6910
7550
  }
6911
7551
  },
6912
- required: [
6913
- 'id',
6914
- 'name',
6915
- 'canonical',
6916
- 'type',
6917
- 'suggestedSegment',
6918
- 'logoUrl',
6919
- 'matchType'
6920
- ]
7552
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
6921
7553
  } as const;
6922
7554
 
6923
- export const $PlatformMatchResponseDto = {
7555
+ export const $PlatformStandardsResponseDto = {
6924
7556
  type: 'object',
6925
7557
  properties: {
6926
- platforms: {
6927
- description: 'Ranked matches, best tier first (at most 10 rows)',
6928
- type: 'array',
6929
- items: {
6930
- $ref: '#/components/schemas/PlatformMatchResultDto'
6931
- }
7558
+ platform: {
7559
+ description: 'The selected platform (institution lock source)',
7560
+ allOf: [
7561
+ {
7562
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7563
+ }
7564
+ ]
6932
7565
  },
6933
- matchType: {
7566
+ region: {
6934
7567
  type: 'string',
6935
7568
  description:
6936
- "Overall match quality — top row's tier, or 'none' when no hits",
6937
- enum: ['none', 'exact', 'prefix', 'substring']
6938
- },
6939
- total: {
6940
- type: 'number',
6941
- description: 'Total matches before LIMIT (truncation transparency)'
7569
+ "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.",
7570
+ example: 'CN'
6942
7571
  },
6943
- hasMore: {
6944
- type: 'boolean',
6945
- description: 'true when total > platforms.length (more matches exist)'
7572
+ templates: {
7573
+ description:
7574
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7575
+ type: 'array',
7576
+ items: {
7577
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7578
+ }
6946
7579
  }
6947
7580
  },
6948
- required: ['platforms', 'matchType', 'total', 'hasMore']
7581
+ required: ['platform', 'region', 'templates']
6949
7582
  } as const;
6950
7583
 
6951
7584
  export const $CreatePlatformDto = {
@@ -7049,7 +7682,8 @@ export const $UpdatePlatformDto = {
7049
7682
  },
7050
7683
  isActive: {
7051
7684
  type: 'boolean',
7052
- description: 'Whether the platform is active'
7685
+ description: 'Whether the platform is active',
7686
+ default: true
7053
7687
  }
7054
7688
  }
7055
7689
  } as const;
@@ -7212,7 +7846,8 @@ export const $AccountItemDto = {
7212
7846
  },
7213
7847
  displayName: {
7214
7848
  type: 'string',
7215
- description: 'Display name (last part of account path)',
7849
+ description:
7850
+ '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)',
7216
7851
  example: 'Savings'
7217
7852
  },
7218
7853
  balance: {
@@ -7248,7 +7883,8 @@ export const $PlatformGroupDto = {
7248
7883
  example: 'CMB Bank'
7249
7884
  },
7250
7885
  accounts: {
7251
- description: 'Accounts within this platform',
7886
+ description:
7887
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7252
7888
  type: 'array',
7253
7889
  items: {
7254
7890
  $ref: '#/components/schemas/AccountItemDto'
@@ -7256,7 +7892,8 @@ export const $PlatformGroupDto = {
7256
7892
  },
7257
7893
  totalBalance: {
7258
7894
  type: 'string',
7259
- description: 'FX-converted total balance in base currency',
7895
+ description:
7896
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7260
7897
  example: '100000.00'
7261
7898
  },
7262
7899
  balanceByCurrency: {
@@ -7275,7 +7912,7 @@ export const $PlatformGroupDto = {
7275
7912
  sharePct: {
7276
7913
  type: 'number',
7277
7914
  description:
7278
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7915
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7279
7916
  example: 42.5
7280
7917
  }
7281
7918
  },
@@ -7323,7 +7960,8 @@ export const $AccountsSummaryDto = {
7323
7960
  properties: {
7324
7961
  totalAccounts: {
7325
7962
  type: 'number',
7326
- description: 'Total number of accounts'
7963
+ description:
7964
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7327
7965
  },
7328
7966
  totalPlatforms: {
7329
7967
  type: 'number',
@@ -7381,7 +8019,8 @@ export const $AccountItemWithAssetClassDto = {
7381
8019
  },
7382
8020
  displayName: {
7383
8021
  type: 'string',
7384
- description: 'Display name (last part of account path)',
8022
+ description:
8023
+ '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)',
7385
8024
  example: 'Savings'
7386
8025
  },
7387
8026
  balance: {
@@ -7867,7 +8506,7 @@ export const $MonetaryDto = {
7867
8506
  example: 'USD'
7868
8507
  },
7869
8508
  baseCcyEquivalent: {
7870
- type: 'object',
8509
+ type: 'string',
7871
8510
  description: 'Converted to user base currency (Decimal string)',
7872
8511
  example: '21600',
7873
8512
  nullable: true
@@ -7943,13 +8582,13 @@ export const $HoldingPnlRowDto = {
7943
8582
  example: 'Assets:US:Broker:AAPL'
7944
8583
  },
7945
8584
  accountCcy: {
7946
- type: 'object',
8585
+ type: 'string',
7947
8586
  description: 'Account settlement currency (ISO 4217), from cost currency',
7948
8587
  nullable: true,
7949
8588
  example: 'USD'
7950
8589
  },
7951
8590
  brokerType: {
7952
- type: 'object',
8591
+ type: 'string',
7953
8592
  description: 'Broker type derived from Platform.type',
7954
8593
  nullable: true,
7955
8594
  example: 'broker'
@@ -7970,7 +8609,7 @@ export const $HoldingPnlRowDto = {
7970
8609
  example: 'EQUITY'
7971
8610
  },
7972
8611
  assetSubClass: {
7973
- type: 'object',
8612
+ type: 'string',
7974
8613
  nullable: true,
7975
8614
  example: 'STOCK'
7976
8615
  },
@@ -8017,14 +8656,14 @@ export const $HoldingPnlRowDto = {
8017
8656
  ]
8018
8657
  },
8019
8658
  unrealizedPnlBase: {
8020
- type: 'object',
8659
+ type: 'string',
8021
8660
  description:
8022
8661
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
8023
8662
  nullable: true,
8024
8663
  example: '6000'
8025
8664
  },
8026
8665
  unrealizedPnlPct: {
8027
- type: 'object',
8666
+ type: 'string',
8028
8667
  description: 'Unrealized P&L % (Decimal string)',
8029
8668
  nullable: true,
8030
8669
  example: '25'
@@ -8048,7 +8687,7 @@ export const $HoldingPnlRowDto = {
8048
8687
  ]
8049
8688
  },
8050
8689
  pctOfInvestedAssets: {
8051
- type: 'object',
8690
+ type: 'string',
8052
8691
  description:
8053
8692
  'Share of invested assets % (Decimal string); only for invested chartTokens',
8054
8693
  nullable: true,
@@ -8093,15 +8732,15 @@ export const $HoldingPnlWarningDto = {
8093
8732
  ]
8094
8733
  },
8095
8734
  symbol: {
8096
- type: 'object',
8735
+ type: 'string',
8097
8736
  nullable: true
8098
8737
  },
8099
8738
  accountId: {
8100
- type: 'object',
8739
+ type: 'string',
8101
8740
  nullable: true
8102
8741
  },
8103
8742
  currency: {
8104
- type: 'object',
8743
+ type: 'string',
8105
8744
  nullable: true
8106
8745
  }
8107
8746
  },
@@ -8165,6 +8804,216 @@ export const $AnonymousLoginResponseDto = {
8165
8804
  required: ['authToken']
8166
8805
  } as const;
8167
8806
 
8807
+ export const $ParserContributionMetaDto = {
8808
+ type: 'object',
8809
+ properties: {
8810
+ institution: {
8811
+ type: 'string',
8812
+ description: 'Institution slug (lowercase kebab-case)',
8813
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8814
+ example: 'icbc'
8815
+ },
8816
+ region: {
8817
+ type: 'string',
8818
+ enum: [
8819
+ 'cn',
8820
+ 'us',
8821
+ 'de',
8822
+ 'fr',
8823
+ 'gb',
8824
+ 'hk',
8825
+ 'jp',
8826
+ 'sg',
8827
+ 'au',
8828
+ 'ca',
8829
+ 'other'
8830
+ ]
8831
+ },
8832
+ accountType: {
8833
+ type: 'string',
8834
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8835
+ },
8836
+ format: {
8837
+ type: 'string',
8838
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8839
+ },
8840
+ institutionDisplayName: {
8841
+ type: 'string',
8842
+ example: '中国工商银行'
8843
+ },
8844
+ encoding: {
8845
+ type: 'string',
8846
+ example: 'utf-8'
8847
+ },
8848
+ delimiter: {
8849
+ type: 'string',
8850
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8851
+ },
8852
+ headerRows: {
8853
+ type: 'number',
8854
+ default: 1,
8855
+ description: 'Header row count; the client omits the field when it is 1'
8856
+ },
8857
+ notes: {
8858
+ type: 'string',
8859
+ maxLength: 2000
8860
+ }
8861
+ },
8862
+ required: ['institution', 'region', 'accountType', 'format']
8863
+ } as const;
8864
+
8865
+ export const $ParserContributionSamplesDto = {
8866
+ type: 'object',
8867
+ properties: {
8868
+ rows: {
8869
+ description:
8870
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8871
+ type: 'array',
8872
+ items: {
8873
+ type: 'object'
8874
+ }
8875
+ },
8876
+ rawHeaders: {
8877
+ type: 'array',
8878
+ items: {
8879
+ type: 'string'
8880
+ }
8881
+ }
8882
+ },
8883
+ required: ['rows']
8884
+ } as const;
8885
+
8886
+ export const $FieldHintDto = {
8887
+ type: 'object',
8888
+ properties: {
8889
+ columnName: {
8890
+ type: 'string',
8891
+ example: '交易日期'
8892
+ },
8893
+ format: {
8894
+ type: 'string',
8895
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8896
+ example: 'yyyy-MM-dd'
8897
+ },
8898
+ signConvention: {
8899
+ type: 'string',
8900
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8901
+ },
8902
+ creditColumn: {
8903
+ type: 'string'
8904
+ },
8905
+ debitColumn: {
8906
+ type: 'string'
8907
+ }
8908
+ },
8909
+ required: ['columnName']
8910
+ } as const;
8911
+
8912
+ export const $ParserContributionFieldHintsDto = {
8913
+ type: 'object',
8914
+ properties: {
8915
+ date: {
8916
+ $ref: '#/components/schemas/FieldHintDto'
8917
+ },
8918
+ amount: {
8919
+ $ref: '#/components/schemas/FieldHintDto'
8920
+ },
8921
+ description: {
8922
+ $ref: '#/components/schemas/FieldHintDto'
8923
+ },
8924
+ balance: {
8925
+ $ref: '#/components/schemas/FieldHintDto'
8926
+ },
8927
+ payee: {
8928
+ $ref: '#/components/schemas/FieldHintDto'
8929
+ },
8930
+ reference: {
8931
+ $ref: '#/components/schemas/FieldHintDto'
8932
+ },
8933
+ category: {
8934
+ $ref: '#/components/schemas/FieldHintDto'
8935
+ }
8936
+ },
8937
+ required: ['date', 'amount']
8938
+ } as const;
8939
+
8940
+ export const $ExpectedTransactionDto = {
8941
+ type: 'object',
8942
+ properties: {
8943
+ date: {
8944
+ type: 'string',
8945
+ example: '2026-08-01'
8946
+ },
8947
+ amount: {
8948
+ type: 'number',
8949
+ example: -45.5
8950
+ },
8951
+ description: {
8952
+ type: 'string',
8953
+ example: '星巴克-***店'
8954
+ },
8955
+ payee: {
8956
+ type: 'string'
8957
+ },
8958
+ category: {
8959
+ type: 'string'
8960
+ }
8961
+ },
8962
+ required: ['date', 'amount', 'description']
8963
+ } as const;
8964
+
8965
+ export const $ParserContributionExamplesDto = {
8966
+ type: 'object',
8967
+ properties: {
8968
+ expectedTransactions: {
8969
+ type: 'array',
8970
+ items: {
8971
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8972
+ }
8973
+ }
8974
+ },
8975
+ required: ['expectedTransactions']
8976
+ } as const;
8977
+
8978
+ export const $ParserContributionRequestDto = {
8979
+ type: 'object',
8980
+ properties: {
8981
+ meta: {
8982
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8983
+ },
8984
+ samples: {
8985
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8986
+ },
8987
+ fieldHints: {
8988
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8989
+ },
8990
+ examples: {
8991
+ description: 'Omitted entirely by the client when empty',
8992
+ allOf: [
8993
+ {
8994
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8995
+ }
8996
+ ]
8997
+ }
8998
+ },
8999
+ required: ['meta', 'samples', 'fieldHints']
9000
+ } as const;
9001
+
9002
+ export const $ParserContributionRelayResponseDto = {
9003
+ type: 'object',
9004
+ properties: {
9005
+ issueUrl: {
9006
+ type: 'string',
9007
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
9008
+ },
9009
+ issueNumber: {
9010
+ type: 'number',
9011
+ example: 42
9012
+ }
9013
+ },
9014
+ required: ['issueUrl', 'issueNumber']
9015
+ } as const;
9016
+
8168
9017
  export const $SymbolSearchResultDto = {
8169
9018
  type: 'object',
8170
9019
  properties: {
@@ -8173,35 +9022,35 @@ export const $SymbolSearchResultDto = {
8173
9022
  example: 'AAPL'
8174
9023
  },
8175
9024
  name: {
8176
- type: 'object',
9025
+ type: 'string',
8177
9026
  example: 'Apple Inc.',
8178
9027
  nullable: true
8179
9028
  },
8180
9029
  exchange: {
8181
- type: 'object',
9030
+ type: 'string',
8182
9031
  example: 'US',
8183
9032
  nullable: true
8184
9033
  },
8185
9034
  assetType: {
8186
- type: 'object',
9035
+ type: 'string',
8187
9036
  description: 'OpenBB asset_type (e.g. stock, etf)',
8188
9037
  example: 'stock',
8189
9038
  nullable: true
8190
9039
  },
8191
9040
  assetClass: {
8192
- type: 'object',
9041
+ type: 'string',
8193
9042
  description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8194
9043
  example: 'EQUITY',
8195
9044
  nullable: true
8196
9045
  },
8197
9046
  assetSubClass: {
8198
- type: 'object',
9047
+ type: 'string',
8199
9048
  description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8200
9049
  example: 'STOCK',
8201
9050
  nullable: true
8202
9051
  },
8203
9052
  currency: {
8204
- type: 'object',
9053
+ type: 'string',
8205
9054
  description: 'Trading currency (extra_data or inferred from exchange)',
8206
9055
  example: 'USD',
8207
9056
  nullable: true
@@ -8218,90 +9067,90 @@ export const $SymbolQuoteDto = {
8218
9067
  example: 'AAPL'
8219
9068
  },
8220
9069
  name: {
8221
- type: 'object',
9070
+ type: 'string',
8222
9071
  example: 'Apple Inc.',
8223
9072
  nullable: true
8224
9073
  },
8225
9074
  exchange: {
8226
- type: 'object',
9075
+ type: 'string',
8227
9076
  example: 'US',
8228
9077
  nullable: true
8229
9078
  },
8230
9079
  assetType: {
8231
- type: 'object',
9080
+ type: 'string',
8232
9081
  description: 'OpenBB asset_type',
8233
9082
  example: 'stock',
8234
9083
  nullable: true
8235
9084
  },
8236
9085
  assetClass: {
8237
- type: 'object',
9086
+ type: 'string',
8238
9087
  description: 'IGN asset class',
8239
9088
  example: 'EQUITY',
8240
9089
  nullable: true
8241
9090
  },
8242
9091
  assetSubClass: {
8243
- type: 'object',
9092
+ type: 'string',
8244
9093
  description: 'IGN asset sub-class',
8245
9094
  example: 'STOCK',
8246
9095
  nullable: true
8247
9096
  },
8248
9097
  currency: {
8249
- type: 'object',
9098
+ type: 'string',
8250
9099
  description: 'Trading currency (extra_data or inferred from exchange)',
8251
9100
  example: 'USD',
8252
9101
  nullable: true
8253
9102
  },
8254
9103
  price: {
8255
- type: 'object',
9104
+ type: 'string',
8256
9105
  description: 'Latest price (Decimal string)',
8257
9106
  example: '189.84',
8258
9107
  nullable: true
8259
9108
  },
8260
9109
  priceDate: {
8261
- type: 'object',
9110
+ type: 'string',
8262
9111
  description: 'Date the price was observed (ISO yyyy-MM-dd)',
8263
9112
  example: '2026-08-05',
8264
9113
  nullable: true
8265
9114
  },
8266
9115
  changePercent: {
8267
- type: 'object',
9116
+ type: 'number',
8268
9117
  description:
8269
9118
  '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.',
8270
9119
  example: 1.7,
8271
9120
  nullable: true
8272
9121
  },
8273
9122
  prevClose: {
8274
- type: 'object',
9123
+ type: 'string',
8275
9124
  description: 'Previous close (Decimal string)',
8276
9125
  nullable: true
8277
9126
  },
8278
9127
  open: {
8279
- type: 'object',
9128
+ type: 'string',
8280
9129
  description: 'Day open (Decimal string)',
8281
9130
  nullable: true
8282
9131
  },
8283
9132
  high: {
8284
- type: 'object',
9133
+ type: 'string',
8285
9134
  description: 'Day high (Decimal string)',
8286
9135
  nullable: true
8287
9136
  },
8288
9137
  low: {
8289
- type: 'object',
9138
+ type: 'string',
8290
9139
  description: 'Day low (Decimal string)',
8291
9140
  nullable: true
8292
9141
  },
8293
9142
  volume: {
8294
- type: 'object',
9143
+ type: 'string',
8295
9144
  description: 'Day volume (Decimal string)',
8296
9145
  nullable: true
8297
9146
  },
8298
9147
  yearHigh: {
8299
- type: 'object',
9148
+ type: 'string',
8300
9149
  description: '52-week high (Decimal string)',
8301
9150
  nullable: true
8302
9151
  },
8303
9152
  yearLow: {
8304
- type: 'object',
9153
+ type: 'string',
8305
9154
  description: '52-week low (Decimal string)',
8306
9155
  nullable: true
8307
9156
  }