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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,14 @@ export const $CreateAccountDto = {
51
51
  description: 'Icon identifier (overrides template)',
52
52
  example: 'bank-custom'
53
53
  },
54
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
54
62
  openDirectiveMeta: {
55
63
  type: 'object',
56
64
  description:
@@ -124,6 +132,8 @@ export const $AccountResponseDto = {
124
132
  'STUDENT_LOAN',
125
133
  'CREDIT_CARD',
126
134
  'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
127
137
  'OTHER'
128
138
  ],
129
139
  nullable: true,
@@ -179,7 +189,8 @@ export const $AccountResponseDto = {
179
189
  },
180
190
  displayName: {
181
191
  type: 'string',
182
- description: 'Localized display name (ADR-0114, read-time projection)',
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
183
194
  example: 'Checking'
184
195
  },
185
196
  icon: {
@@ -195,7 +206,7 @@ export const $AccountResponseDto = {
195
206
  }
196
207
  },
197
208
  platformId: {
198
- type: 'object',
209
+ type: 'string',
199
210
  description: 'Platform ID (null if unbound)',
200
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
201
212
  },
@@ -275,6 +286,14 @@ export const $UpdateAccountDto = {
275
286
  description: 'Icon identifier',
276
287
  example: 'bank-custom'
277
288
  },
289
+ displayName: {
290
+ type: 'string',
291
+ description:
292
+ 'User-set display name override (null = clear the override and fall back to the derived name, omit = unchanged)',
293
+ nullable: true,
294
+ maxLength: 50,
295
+ example: 'Salary card'
296
+ },
278
297
  openDirectiveMeta: {
279
298
  type: 'object',
280
299
  description:
@@ -375,16 +394,43 @@ export const $AccountStandardResponseDto = {
375
394
  },
376
395
  name: {
377
396
  type: 'string',
378
- description: 'Short localized display name',
397
+ description:
398
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
379
399
  example: 'Housing Fund'
380
400
  },
401
+ aliases: {
402
+ description:
403
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
404
+ example: ['Alipay', 'WeChat Pay'],
405
+ type: 'array',
406
+ items: {
407
+ type: 'string'
408
+ }
409
+ },
410
+ searchTerms: {
411
+ description:
412
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
413
+ example: ['yinhangka', 'jiejika'],
414
+ type: 'array',
415
+ items: {
416
+ type: 'string'
417
+ }
418
+ },
419
+ currency: {
420
+ type: 'string',
421
+ description:
422
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
423
+ example: 'HKD'
424
+ },
381
425
  description: {
382
426
  type: 'string',
383
- description: 'Account description (stable semantics only)',
427
+ description:
428
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
384
429
  example: 'ICBC checking account for daily transactions'
385
430
  },
386
431
  tags: {
387
- description: 'Account tags for categorization',
432
+ description:
433
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
388
434
  example: ['bank', 'checking', 'primary'],
389
435
  type: 'array',
390
436
  items: {
@@ -395,9 +441,47 @@ export const $AccountStandardResponseDto = {
395
441
  type: 'string',
396
442
  description: 'Icon identifier for UI display',
397
443
  example: 'bank-icbc'
444
+ },
445
+ productCategory: {
446
+ type: 'string',
447
+ description:
448
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
449
+ enum: [
450
+ 'cash',
451
+ 'investment',
452
+ 'credit_card',
453
+ 'loan',
454
+ 'payable_tax',
455
+ 'other'
456
+ ],
457
+ example: 'investment'
458
+ },
459
+ assetClass: {
460
+ type: 'string',
461
+ description:
462
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
463
+ enum: [
464
+ 'LIQUIDITY',
465
+ 'EQUITY',
466
+ 'FIXED_INCOME',
467
+ 'PRECIOUS_METALS',
468
+ 'COMMODITY',
469
+ 'INSURANCE',
470
+ 'ALTERNATIVE_INVESTMENT',
471
+ 'PERSONAL_ASSETS',
472
+ 'LIABILITY',
473
+ 'REAL_ESTATE',
474
+ 'INDEX'
475
+ ]
476
+ },
477
+ assetSubClass: {
478
+ type: 'string',
479
+ description:
480
+ 'Asset sub-class (product type, derived at read time from classification rules)',
481
+ example: 'STOCK'
398
482
  }
399
483
  },
400
- required: ['path', 'type', 'description', 'tags', 'icon']
484
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
401
485
  } as const;
402
486
 
403
487
  export const $AccountStandardListResponseDto = {
@@ -458,7 +542,10 @@ export const $RegionConfigDto = {
458
542
  },
459
543
  locale: {
460
544
  type: 'string',
461
- example: 'de-DE'
545
+ example: 'de-DE',
546
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
547
+ description:
548
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
462
549
  }
463
550
  },
464
551
  required: ['currency', 'dateFormat', 'locale']
@@ -471,6 +558,12 @@ export const $RegionInfoDto = {
471
558
  type: 'string',
472
559
  example: 'de'
473
560
  },
561
+ open: {
562
+ type: 'boolean',
563
+ example: true,
564
+ description:
565
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
566
+ },
474
567
  displayName: {
475
568
  type: 'string',
476
569
  example: 'Germany'
@@ -489,7 +582,7 @@ export const $RegionInfoDto = {
489
582
  $ref: '#/components/schemas/RegionConfigDto'
490
583
  }
491
584
  },
492
- required: ['code', 'displayName', 'chain', 'config']
585
+ required: ['code', 'open', 'displayName', 'chain', 'config']
493
586
  } as const;
494
587
 
495
588
  export const $RegionsMetadataResponseDto = {
@@ -736,7 +829,7 @@ export const $PostingResponseDto = {
736
829
  units: {
737
830
  type: 'string',
738
831
  description:
739
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
832
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
740
833
  example: '100.50'
741
834
  },
742
835
  currency: {
@@ -1102,7 +1195,7 @@ export const $PostingDetailDto = {
1102
1195
  units: {
1103
1196
  type: 'string',
1104
1197
  description:
1105
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1198
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
1106
1199
  example: '100.50'
1107
1200
  },
1108
1201
  currency: {
@@ -1286,6 +1379,147 @@ export const $TransactionDetailDto = {
1286
1379
  ]
1287
1380
  } as const;
1288
1381
 
1382
+ export const $TransactionListItemDto = {
1383
+ type: 'object',
1384
+ properties: {
1385
+ id: {
1386
+ type: 'string',
1387
+ description: 'Transaction ID',
1388
+ example: 'clh1234567890abcdef'
1389
+ },
1390
+ date: {
1391
+ type: 'string',
1392
+ description: 'Transaction date',
1393
+ example: '2024-11-28'
1394
+ },
1395
+ flag: {
1396
+ type: 'string',
1397
+ description: 'Transaction flag',
1398
+ enum: [
1399
+ 'CLEARED',
1400
+ 'PENDING',
1401
+ 'PADDING',
1402
+ 'SUMMARIZE',
1403
+ 'TRANSFER',
1404
+ 'CONVERSIONS'
1405
+ ],
1406
+ example: 'CLEARED'
1407
+ },
1408
+ customFlag: {
1409
+ type: 'string',
1410
+ description: 'Custom flag (if not using standard flags)',
1411
+ example: 'R'
1412
+ },
1413
+ payee: {
1414
+ type: 'string',
1415
+ description: 'Payee name',
1416
+ example: 'Whole Foods Market'
1417
+ },
1418
+ narration: {
1419
+ type: 'string',
1420
+ description: 'Transaction narration',
1421
+ example: 'Grocery shopping'
1422
+ },
1423
+ tags: {
1424
+ description: 'Transaction tags',
1425
+ example: ['groceries'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ links: {
1432
+ description: 'Transaction links',
1433
+ example: ['invoice-2024-001'],
1434
+ type: 'array',
1435
+ items: {
1436
+ type: 'string'
1437
+ }
1438
+ },
1439
+ meta: {
1440
+ type: 'object',
1441
+ description: 'Transaction metadata'
1442
+ },
1443
+ status: {
1444
+ type: 'string',
1445
+ description: 'Transaction status',
1446
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1447
+ example: 'ACTIVE'
1448
+ },
1449
+ sourceType: {
1450
+ type: 'string',
1451
+ description:
1452
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1453
+ },
1454
+ sourcePlatform: {
1455
+ type: 'string',
1456
+ description: 'Source platform (e.g., alipay, wechat)',
1457
+ example: 'alipay'
1458
+ },
1459
+ postings: {
1460
+ description: 'Transaction postings',
1461
+ type: 'array',
1462
+ items: {
1463
+ $ref: '#/components/schemas/PostingDetailDto'
1464
+ }
1465
+ },
1466
+ createdAt: {
1467
+ type: 'string',
1468
+ description: 'Created at timestamp',
1469
+ example: '2024-11-28T10:30:00.000Z'
1470
+ },
1471
+ voidedAt: {
1472
+ type: 'string',
1473
+ description: 'Voided at timestamp (if voided)',
1474
+ example: '2024-11-29T15:00:00.000Z'
1475
+ },
1476
+ voidedBy: {
1477
+ type: 'string',
1478
+ description: 'User ID who voided this transaction',
1479
+ example: 'clh1234567890abcdef'
1480
+ },
1481
+ correctionReason: {
1482
+ type: 'string',
1483
+ description: 'Correction reason (if voided or superseded)',
1484
+ example: 'Duplicate entry'
1485
+ },
1486
+ supersededBy: {
1487
+ type: 'string',
1488
+ description:
1489
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1490
+ example: 'clh1234567890abcdef'
1491
+ },
1492
+ originalTxn: {
1493
+ type: 'string',
1494
+ description:
1495
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1496
+ example: 'clh1234567890abcdef'
1497
+ },
1498
+ viewpointAmount: {
1499
+ type: 'string',
1500
+ description:
1501
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1502
+ example: '10000.00'
1503
+ },
1504
+ viewpointCurrency: {
1505
+ type: 'string',
1506
+ description:
1507
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1508
+ example: 'CNY'
1509
+ }
1510
+ },
1511
+ required: [
1512
+ 'id',
1513
+ 'date',
1514
+ 'narration',
1515
+ 'tags',
1516
+ 'links',
1517
+ 'status',
1518
+ 'postings',
1519
+ 'createdAt'
1520
+ ]
1521
+ } as const;
1522
+
1289
1523
  export const $BalanceByCurrencyDto = {
1290
1524
  type: 'object',
1291
1525
  properties: {
@@ -1331,7 +1565,7 @@ export const $TransactionListSummaryDto = {
1331
1565
  totalAmount: {
1332
1566
  type: 'string',
1333
1567
  description:
1334
- 'Partial converted total in base currency (rated currencies only, raw Beancount sign). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1568
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1335
1569
  example: '-6000.00'
1336
1570
  },
1337
1571
  currency: {
@@ -1357,6 +1591,26 @@ export const $TransactionListSummaryDto = {
1357
1591
  required: ['totalAmount', 'currency', 'balanceByCurrency']
1358
1592
  } as const;
1359
1593
 
1594
+ export const $TransactionListViewpointDto = {
1595
+ type: 'object',
1596
+ properties: {
1597
+ type: {
1598
+ type: 'string',
1599
+ description:
1600
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1601
+ enum: ['category'],
1602
+ example: 'category'
1603
+ },
1604
+ flow: {
1605
+ type: 'string',
1606
+ description: 'Flow root the category account set is restricted to',
1607
+ enum: ['income', 'expense'],
1608
+ example: 'expense'
1609
+ }
1610
+ },
1611
+ required: ['type', 'flow']
1612
+ } as const;
1613
+
1360
1614
  export const $TransactionListResponseDto = {
1361
1615
  type: 'object',
1362
1616
  properties: {
@@ -1364,7 +1618,7 @@ export const $TransactionListResponseDto = {
1364
1618
  description: 'List of transactions',
1365
1619
  type: 'array',
1366
1620
  items: {
1367
- $ref: '#/components/schemas/TransactionDetailDto'
1621
+ $ref: '#/components/schemas/TransactionListItemDto'
1368
1622
  }
1369
1623
  },
1370
1624
  total: {
@@ -1390,6 +1644,15 @@ export const $TransactionListResponseDto = {
1390
1644
  $ref: '#/components/schemas/TransactionListSummaryDto'
1391
1645
  }
1392
1646
  ]
1647
+ },
1648
+ viewpoint: {
1649
+ description:
1650
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1651
+ allOf: [
1652
+ {
1653
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1654
+ }
1655
+ ]
1393
1656
  }
1394
1657
  },
1395
1658
  required: ['data', 'total', 'limit', 'offset']
@@ -2300,10 +2563,10 @@ export const $UpdatePayeeDto = {
2300
2563
  meta: {
2301
2564
  type: 'object',
2302
2565
  description:
2303
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2566
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2304
2567
  example: {
2305
2568
  location: 'Zhongguancun',
2306
- note: 'Updated note',
2569
+ note: 'Near subway station',
2307
2570
  favorite: true
2308
2571
  }
2309
2572
  },
@@ -2864,568 +3127,660 @@ export const $UpdateCommodityDto = {
2864
3127
  }
2865
3128
  } as const;
2866
3129
 
2867
- export const $CreateBeanPriceDto = {
3130
+ export const $CurrencyBalanceDto = {
2868
3131
  type: 'object',
2869
3132
  properties: {
2870
3133
  currency: {
2871
3134
  type: 'string',
2872
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2873
- example: 'USD'
2874
- },
2875
- quoteCurrency: {
2876
- type: 'string',
2877
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3135
+ description: 'ISO 4217 currency code',
2878
3136
  example: 'CNY'
2879
3137
  },
2880
- amount: {
2881
- type: 'number',
2882
- description:
2883
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
2884
- example: 175.5,
2885
- minimum: 0
2886
- },
2887
- date: {
3138
+ balance: {
2888
3139
  type: 'string',
2889
- description: 'Price date (ISO 8601 format)',
2890
- example: '2024-11-05'
2891
- },
2892
- metadata: {
2893
- type: 'object',
2894
- description:
2895
- 'Metadata (validated by Zod schema, max field lengths enforced)',
2896
- example: {
2897
- source: 'MANUAL',
2898
- note: 'Bank valuation report',
2899
- confidence: 0.95
2900
- }
3140
+ description: 'Balance amount',
3141
+ example: '500000.00'
2901
3142
  }
2902
3143
  },
2903
- required: ['currency', 'quoteCurrency', 'amount', 'date']
3144
+ required: ['currency', 'balance']
2904
3145
  } as const;
2905
3146
 
2906
- export const $PriceResponseDto = {
3147
+ export const $TimeSeriesPointDto = {
2907
3148
  type: 'object',
2908
3149
  properties: {
2909
- id: {
3150
+ date: {
2910
3151
  type: 'string',
2911
- description: 'Unique identifier',
2912
- example: 'uuid-123-456'
3152
+ description: 'Date in YYYY-MM-DD format',
3153
+ example: '2024-06-15'
2913
3154
  },
2914
- userId: {
3155
+ value: {
2915
3156
  type: 'string',
2916
- description: 'User ID (owner of the price)',
2917
- example: 'user-123'
3157
+ description: 'Value at this date (in base currency)',
3158
+ example: '500000.00'
2918
3159
  },
2919
- currency: {
3160
+ change: {
2920
3161
  type: 'string',
2921
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2922
- example: 'BTC'
3162
+ description: 'Change from previous point',
3163
+ example: '5000.00'
2923
3164
  },
2924
- quoteCurrency: {
3165
+ assets: {
2925
3166
  type: 'string',
2926
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
2927
- example: 'USD'
2928
- },
2929
- amount: {
2930
- type: 'number',
2931
- description:
2932
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
2933
- example: 50000
3167
+ description: 'Total assets at this date (in base currency)',
3168
+ example: '494338.00'
2934
3169
  },
2935
- date: {
3170
+ liabilities: {
2936
3171
  type: 'string',
2937
- description:
2938
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
2939
- example: '2024-01-01',
2940
- format: 'date'
3172
+ description: 'Total liabilities at this date (in base currency)',
3173
+ example: '310098.00'
2941
3174
  },
2942
- meta: {
2943
- type: 'object',
2944
- description:
2945
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
2946
- example: {
2947
- source: 'MANUAL',
2948
- note: 'User-defined price',
2949
- confidence: 1
3175
+ byCurrency: {
3176
+ description: 'Multi-currency breakdown for this point',
3177
+ type: 'array',
3178
+ items: {
3179
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2950
3180
  }
3181
+ }
3182
+ },
3183
+ required: ['date', 'value']
3184
+ } as const;
3185
+
3186
+ export const $TrendSummaryDto = {
3187
+ type: 'object',
3188
+ properties: {
3189
+ startValue: {
3190
+ type: 'string',
3191
+ description: 'Value at start of period',
3192
+ example: '450000.00'
2951
3193
  },
2952
- createdAt: {
2953
- format: 'date-time',
3194
+ endValue: {
2954
3195
  type: 'string',
2955
- description: 'Creation timestamp',
2956
- example: '2024-11-03T10:00:00Z'
3196
+ description: 'Value at end of period',
3197
+ example: '500000.00'
2957
3198
  },
2958
- updatedAt: {
2959
- format: 'date-time',
3199
+ totalChange: {
2960
3200
  type: 'string',
2961
- description: 'Last update timestamp',
2962
- example: '2024-11-03T10:00:00Z'
3201
+ description: 'Total change over period',
3202
+ example: '50000.00'
3203
+ },
3204
+ totalChangePercentage: {
3205
+ type: 'string',
3206
+ description: 'Total change percentage',
3207
+ example: '+11.11%'
2963
3208
  }
2964
3209
  },
2965
- required: [
2966
- 'id',
2967
- 'userId',
2968
- 'currency',
2969
- 'quoteCurrency',
2970
- 'amount',
2971
- 'date',
2972
- 'meta',
2973
- 'createdAt',
2974
- 'updatedAt'
2975
- ]
3210
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2976
3211
  } as const;
2977
3212
 
2978
- export const $PriceListResponseDto = {
3213
+ export const $MultiCurrencyPointDto = {
2979
3214
  type: 'object',
2980
3215
  properties: {
2981
- items: {
2982
- description: 'List of prices',
3216
+ date: {
3217
+ type: 'string',
3218
+ description: 'Date in YYYY-MM-DD format',
3219
+ example: '2024-06-15'
3220
+ },
3221
+ byCurrency: {
3222
+ description: 'Balances by currency',
2983
3223
  type: 'array',
2984
3224
  items: {
2985
- $ref: '#/components/schemas/PriceResponseDto'
3225
+ $ref: '#/components/schemas/CurrencyBalanceDto'
2986
3226
  }
2987
- },
2988
- total: {
2989
- type: 'number',
2990
- description: 'Total number of prices',
2991
- example: 42
2992
3227
  }
2993
3228
  },
2994
- required: ['items', 'total']
3229
+ required: ['date', 'byCurrency']
2995
3230
  } as const;
2996
3231
 
2997
- export const $UpdateBeanPriceDto = {
3232
+ export const $PortfolioTrendsResponseDto = {
2998
3233
  type: 'object',
2999
3234
  properties: {
3000
- currency: {
3001
- type: 'string',
3002
- description: 'Currency being priced'
3235
+ series: {
3236
+ description: 'Time series data points',
3237
+ type: 'array',
3238
+ items: {
3239
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3240
+ }
3003
3241
  },
3004
- quoteCurrency: {
3242
+ summary: {
3243
+ description: 'Period summary',
3244
+ allOf: [
3245
+ {
3246
+ $ref: '#/components/schemas/TrendSummaryDto'
3247
+ }
3248
+ ]
3249
+ },
3250
+ period: {
3005
3251
  type: 'string',
3006
- description: 'Quote currency (pricing currency)'
3252
+ description: 'Period requested',
3253
+ example: '6m'
3007
3254
  },
3008
- amount: {
3009
- type: 'number',
3010
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
3011
- minimum: 0
3255
+ granularity: {
3256
+ type: 'string',
3257
+ description: 'Data granularity',
3258
+ example: 'month'
3012
3259
  },
3013
- date: {
3260
+ currency: {
3014
3261
  type: 'string',
3015
- description: 'Price date (ISO 8601 format)'
3262
+ description: 'Base currency for converted values',
3263
+ example: 'CNY'
3016
3264
  },
3017
- metadata: {
3018
- type: 'object',
3019
- description: 'Metadata'
3265
+ byCurrency: {
3266
+ description:
3267
+ 'Multi-currency time series (each point has currency breakdown)',
3268
+ type: 'array',
3269
+ items: {
3270
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3271
+ }
3272
+ },
3273
+ warnings: {
3274
+ description: 'Exchange rate warnings',
3275
+ type: 'array',
3276
+ items: {
3277
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3278
+ }
3020
3279
  }
3021
- }
3280
+ },
3281
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3022
3282
  } as const;
3023
3283
 
3024
- export const $CreateRecurringRuleDto = {
3284
+ export const $CashFlowPointDto = {
3025
3285
  type: 'object',
3026
3286
  properties: {
3027
- name: {
3287
+ month: {
3028
3288
  type: 'string',
3029
- description: 'Rule name (unique per user)',
3030
- maxLength: 100
3289
+ description: 'Month key (YYYY-MM)',
3290
+ example: '2024-03'
3031
3291
  },
3032
- icon: {
3292
+ income: {
3033
3293
  type: 'string',
3034
- description: 'Icon emoji',
3035
- maxLength: 10
3294
+ description: 'Income in base currency (absolute, converted)',
3295
+ example: '10000.00'
3036
3296
  },
3037
- frequency: {
3297
+ expense: {
3038
3298
  type: 'string',
3039
- description: 'Recurring frequency',
3040
- enum: [
3041
- 'WEEKLY',
3042
- 'BIWEEKLY',
3043
- 'MONTHLY',
3044
- 'BIMONTHLY',
3045
- 'QUARTERLY',
3046
- 'YEARLY',
3047
- 'CUSTOM'
3048
- ]
3049
- },
3050
- expectedAmount: {
3051
- type: 'number',
3052
- description: 'Expected amount (positive number)',
3053
- minimum: 0
3054
- },
3055
- expectedDay: {
3056
- type: 'number',
3057
- description: 'Expected day of month (1-31)',
3058
- minimum: 1,
3059
- maximum: 31
3060
- },
3061
- customIntervalDays: {
3062
- type: 'number',
3063
- description: 'Custom interval in days (required for CUSTOM frequency)',
3064
- minimum: 1
3299
+ description: 'Expense in base currency (absolute, converted)',
3300
+ example: '5000.00'
3065
3301
  },
3066
- currency: {
3302
+ netSavings: {
3067
3303
  type: 'string',
3068
- description: 'Currency code',
3069
- default: 'CNY',
3070
- maxLength: 10
3071
- },
3072
- matchPayeePattern: {
3304
+ description: 'netSavings = income − expense (savings positive)',
3305
+ example: '5000.00'
3306
+ }
3307
+ },
3308
+ required: ['month', 'income', 'expense', 'netSavings']
3309
+ } as const;
3310
+
3311
+ export const $CashFlowTrendSummaryDto = {
3312
+ type: 'object',
3313
+ properties: {
3314
+ totalIncome: {
3073
3315
  type: 'string',
3074
- description: 'Payee matching pattern (supports wildcards)',
3075
- maxLength: 200
3076
- },
3077
- matchAmountTolerance: {
3078
- type: 'number',
3079
- description: 'Amount tolerance percentage (0-1)',
3080
- default: 0.075,
3081
- minimum: 0,
3082
- maximum: 1
3316
+ description: 'Total income across the period',
3317
+ example: '60000.00'
3083
3318
  },
3084
- defaultExpenseAccount: {
3319
+ totalExpense: {
3085
3320
  type: 'string',
3086
- description: 'Default expense account for auto-create',
3087
- maxLength: 200
3321
+ description: 'Total expense across the period',
3322
+ example: '30000.00'
3088
3323
  },
3089
- defaultPaymentAccount: {
3324
+ totalNetSavings: {
3090
3325
  type: 'string',
3091
- description: 'Default payment account for auto-create',
3092
- maxLength: 200
3326
+ description: 'income − expense across the period',
3327
+ example: '30000.00'
3093
3328
  },
3094
- defaultPayee: {
3095
- type: 'string',
3096
- description: 'Default payee for auto-create',
3097
- maxLength: 200
3098
- },
3099
- autoCreate: {
3100
- type: 'boolean',
3101
- description: 'Auto-create transaction when expected date arrives',
3102
- default: false
3103
- },
3104
- startDate: {
3105
- type: 'string',
3106
- description: 'Rule start date (ISO format)'
3107
- },
3108
- endDate: {
3329
+ averageMonthlyNetSavings: {
3109
3330
  type: 'string',
3110
- description: 'Rule end date (ISO format)'
3331
+ description:
3332
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3333
+ example: '5000.00'
3111
3334
  }
3112
3335
  },
3113
3336
  required: [
3114
- 'name',
3115
- 'frequency',
3116
- 'expectedAmount',
3117
- 'currency',
3118
- 'matchAmountTolerance',
3119
- 'autoCreate'
3337
+ 'totalIncome',
3338
+ 'totalExpense',
3339
+ 'totalNetSavings',
3340
+ 'averageMonthlyNetSavings'
3120
3341
  ]
3121
3342
  } as const;
3122
3343
 
3123
- export const $RecurringRuleResponseDto = {
3344
+ export const $CashFlowTrendsResponseDto = {
3124
3345
  type: 'object',
3125
3346
  properties: {
3126
- id: {
3127
- type: 'string',
3128
- description: 'Rule ID'
3347
+ series: {
3348
+ description:
3349
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3350
+ type: 'array',
3351
+ items: {
3352
+ $ref: '#/components/schemas/CashFlowPointDto'
3353
+ }
3129
3354
  },
3130
- userId: {
3131
- type: 'string',
3132
- description: 'User ID'
3355
+ summary: {
3356
+ description: 'Period totals',
3357
+ allOf: [
3358
+ {
3359
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3360
+ }
3361
+ ]
3133
3362
  },
3134
- name: {
3363
+ period: {
3135
3364
  type: 'string',
3136
- description: 'Rule name'
3137
- },
3138
- icon: {
3139
- type: 'object',
3140
- description: 'Icon emoji'
3365
+ description: 'Period requested',
3366
+ example: '6m'
3141
3367
  },
3142
- frequency: {
3368
+ granularity: {
3143
3369
  type: 'string',
3144
- description: 'Recurring frequency'
3145
- },
3146
- expectedAmount: {
3147
- type: 'number',
3148
- description: 'Expected amount'
3149
- },
3150
- expectedDay: {
3151
- type: 'object',
3152
- description: 'Expected day of month'
3370
+ description: 'Data granularity (v1 returns month buckets)',
3371
+ example: 'month'
3153
3372
  },
3154
- customIntervalDays: {
3155
- type: 'object',
3156
- description: 'Custom interval in days'
3373
+ currency: {
3374
+ type: 'string',
3375
+ description: 'Base currency for converted values',
3376
+ example: 'CNY'
3157
3377
  },
3378
+ warnings: {
3379
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3380
+ type: 'array',
3381
+ items: {
3382
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3383
+ }
3384
+ }
3385
+ },
3386
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3387
+ } as const;
3388
+
3389
+ export const $GenerateSnapshotBody = {
3390
+ type: 'object',
3391
+ properties: {}
3392
+ } as const;
3393
+
3394
+ export const $GenerateSnapshotResponse = {
3395
+ type: 'object',
3396
+ properties: {}
3397
+ } as const;
3398
+
3399
+ export const $BackfillSnapshotsBody = {
3400
+ type: 'object',
3401
+ properties: {}
3402
+ } as const;
3403
+
3404
+ export const $BackfillSnapshotsResponse = {
3405
+ type: 'object',
3406
+ properties: {}
3407
+ } as const;
3408
+
3409
+ export const $CreateBeanPriceDto = {
3410
+ type: 'object',
3411
+ properties: {
3158
3412
  currency: {
3159
3413
  type: 'string',
3160
- description: 'Currency code'
3414
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3415
+ example: 'USD'
3161
3416
  },
3162
- matchPayeePattern: {
3163
- type: 'object',
3164
- description: 'Payee matching pattern'
3417
+ quoteCurrency: {
3418
+ type: 'string',
3419
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3420
+ example: 'CNY'
3165
3421
  },
3166
- matchAmountTolerance: {
3422
+ amount: {
3167
3423
  type: 'number',
3168
- description: 'Amount tolerance percentage'
3424
+ description:
3425
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3426
+ example: 175.5,
3427
+ minimum: 0
3169
3428
  },
3170
- defaultExpenseAccount: {
3171
- type: 'object',
3172
- description: 'Default expense account'
3429
+ date: {
3430
+ type: 'string',
3431
+ description: 'Price date (ISO 8601 format)',
3432
+ example: '2024-11-05'
3173
3433
  },
3174
- defaultPaymentAccount: {
3434
+ metadata: {
3175
3435
  type: 'object',
3176
- description: 'Default payment account'
3436
+ description:
3437
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3438
+ example: {
3439
+ source: 'MANUAL',
3440
+ note: 'Bank valuation report',
3441
+ confidence: 0.95
3442
+ }
3443
+ }
3444
+ },
3445
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3446
+ } as const;
3447
+
3448
+ export const $PriceResponseDto = {
3449
+ type: 'object',
3450
+ properties: {
3451
+ id: {
3452
+ type: 'string',
3453
+ description: 'Unique identifier',
3454
+ example: 'uuid-123-456'
3177
3455
  },
3178
- defaultPayee: {
3179
- type: 'object',
3180
- description: 'Default payee'
3456
+ userId: {
3457
+ type: 'string',
3458
+ description: 'User ID (owner of the price)',
3459
+ example: 'user-123'
3181
3460
  },
3182
- isActive: {
3183
- type: 'boolean',
3184
- description: 'Whether rule is active'
3461
+ currency: {
3462
+ type: 'string',
3463
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3464
+ example: 'BTC'
3185
3465
  },
3186
- startDate: {
3466
+ quoteCurrency: {
3187
3467
  type: 'string',
3188
- description: 'Rule start date (YYYY-MM-DD)'
3468
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3469
+ example: 'USD'
3189
3470
  },
3190
- endDate: {
3191
- type: 'object',
3192
- description: 'Rule end date (YYYY-MM-DD)'
3471
+ amount: {
3472
+ type: 'number',
3473
+ description:
3474
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3475
+ example: 50000
3193
3476
  },
3194
- autoCreate: {
3195
- type: 'boolean',
3196
- description: 'Auto-create transaction on expected date'
3477
+ date: {
3478
+ type: 'string',
3479
+ description:
3480
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3481
+ example: '2024-01-01',
3482
+ format: 'date'
3197
3483
  },
3198
- lastOccurrence: {
3484
+ meta: {
3199
3485
  type: 'object',
3200
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3201
- },
3202
- totalCount: {
3203
- type: 'number',
3204
- description: 'Total matched transactions count'
3486
+ description:
3487
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3488
+ example: {
3489
+ source: 'MANUAL',
3490
+ note: 'User-defined price',
3491
+ confidence: 1
3492
+ }
3205
3493
  },
3206
3494
  createdAt: {
3207
3495
  format: 'date-time',
3208
3496
  type: 'string',
3209
- description: 'Created at timestamp'
3497
+ description: 'Creation timestamp',
3498
+ example: '2024-11-03T10:00:00Z'
3210
3499
  },
3211
3500
  updatedAt: {
3212
3501
  format: 'date-time',
3213
3502
  type: 'string',
3214
- description: 'Updated at timestamp'
3503
+ description: 'Last update timestamp',
3504
+ example: '2024-11-03T10:00:00Z'
3215
3505
  }
3216
3506
  },
3217
3507
  required: [
3218
3508
  'id',
3219
3509
  'userId',
3220
- 'name',
3221
- 'frequency',
3222
- 'expectedAmount',
3223
3510
  'currency',
3224
- 'matchAmountTolerance',
3225
- 'isActive',
3226
- 'startDate',
3227
- 'autoCreate',
3228
- 'totalCount',
3511
+ 'quoteCurrency',
3512
+ 'amount',
3513
+ 'date',
3514
+ 'meta',
3229
3515
  'createdAt',
3230
3516
  'updatedAt'
3231
3517
  ]
3232
3518
  } as const;
3233
3519
 
3234
- export const $CreateRuleFromTransactionDto = {
3520
+ export const $PriceListResponseDto = {
3235
3521
  type: 'object',
3236
3522
  properties: {
3237
- frequency: {
3238
- type: 'string',
3239
- description: 'Recurring frequency',
3240
- enum: [
3241
- 'WEEKLY',
3242
- 'BIWEEKLY',
3243
- 'MONTHLY',
3244
- 'BIMONTHLY',
3245
- 'QUARTERLY',
3246
- 'YEARLY',
3247
- 'CUSTOM'
3248
- ],
3249
- example: 'MONTHLY'
3250
- },
3251
- name: {
3252
- type: 'string',
3253
- description: 'Optional name override (default: transaction payee)',
3254
- maxLength: 100
3523
+ items: {
3524
+ description: 'List of prices',
3525
+ type: 'array',
3526
+ items: {
3527
+ $ref: '#/components/schemas/PriceResponseDto'
3528
+ }
3255
3529
  },
3256
- icon: {
3257
- type: 'string',
3258
- description: 'Optional icon emoji',
3259
- maxLength: 10
3530
+ total: {
3531
+ type: 'number',
3532
+ description: 'Total number of prices',
3533
+ example: 42
3260
3534
  }
3261
3535
  },
3262
- required: ['frequency']
3536
+ required: ['items', 'total']
3263
3537
  } as const;
3264
3538
 
3265
- export const $RecurringRuleWithStatsResponseDto = {
3539
+ export const $UpdateBeanPriceDto = {
3266
3540
  type: 'object',
3267
3541
  properties: {
3268
- id: {
3542
+ currency: {
3269
3543
  type: 'string',
3270
- description: 'Rule ID'
3544
+ description: 'Currency being priced'
3271
3545
  },
3272
- userId: {
3546
+ quoteCurrency: {
3273
3547
  type: 'string',
3274
- description: 'User ID'
3548
+ description: 'Quote currency (pricing currency)'
3275
3549
  },
3276
- name: {
3550
+ amount: {
3551
+ type: 'number',
3552
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3553
+ minimum: 0
3554
+ },
3555
+ date: {
3277
3556
  type: 'string',
3278
- description: 'Rule name'
3557
+ description: 'Price date (ISO 8601 format)'
3279
3558
  },
3280
- icon: {
3559
+ metadata: {
3281
3560
  type: 'object',
3282
- description: 'Icon emoji'
3283
- },
3284
- frequency: {
3561
+ description: 'Metadata'
3562
+ }
3563
+ }
3564
+ } as const;
3565
+
3566
+ export const $DeleteOwnUserDto = {
3567
+ type: 'object',
3568
+ properties: {
3569
+ accessToken: {
3285
3570
  type: 'string',
3286
- description: 'Recurring frequency'
3287
- },
3288
- expectedAmount: {
3289
- type: 'number',
3290
- description: 'Expected amount'
3571
+ description: 'Access token for user verification',
3572
+ example: 'abc123xyz'
3573
+ }
3574
+ },
3575
+ required: ['accessToken']
3576
+ } as const;
3577
+
3578
+ export const $UserSettingsResponseDto = {
3579
+ type: 'object',
3580
+ properties: {
3581
+ baseCurrency: {
3582
+ type: 'string',
3583
+ description:
3584
+ 'Stored base currency choice (ISO 4217) for net-worth/report aggregation. null = user never chose; aggregates fall back to the region default at display time (#713).',
3585
+ example: 'USD',
3586
+ nullable: true
3587
+ }
3588
+ },
3589
+ required: ['baseCurrency']
3590
+ } as const;
3591
+
3592
+ export const $UserResponseDto = {
3593
+ type: 'object',
3594
+ properties: {
3595
+ id: {
3596
+ type: 'string',
3597
+ description: 'User ID'
3291
3598
  },
3292
- expectedDay: {
3293
- type: 'object',
3294
- description: 'Expected day of month'
3599
+ role: {
3600
+ type: 'string',
3601
+ description: 'Assigned user role'
3295
3602
  },
3296
- customIntervalDays: {
3297
- type: 'object',
3298
- description: 'Custom interval in days'
3603
+ permissions: {
3604
+ description: 'Permission strings',
3605
+ type: 'array',
3606
+ items: {
3607
+ type: 'string'
3608
+ }
3299
3609
  },
3300
- currency: {
3610
+ settings: {
3611
+ description: 'User settings',
3612
+ allOf: [
3613
+ {
3614
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3615
+ }
3616
+ ]
3617
+ }
3618
+ },
3619
+ required: ['id', 'role', 'permissions', 'settings']
3620
+ } as const;
3621
+
3622
+ export const $SignupDto = {
3623
+ type: 'object',
3624
+ properties: {
3625
+ turnstileToken: {
3301
3626
  type: 'string',
3302
- description: 'Currency code'
3627
+ description:
3628
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3629
+ example: '0.abc123def456...'
3630
+ }
3631
+ }
3632
+ } as const;
3633
+
3634
+ export const $SignupResponseDto = {
3635
+ type: 'object',
3636
+ properties: {
3637
+ authToken: {
3638
+ type: 'string',
3639
+ description: 'JWT auth token',
3640
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3303
3641
  },
3304
- matchPayeePattern: {
3305
- type: 'object',
3306
- description: 'Payee matching pattern'
3642
+ accessToken: {
3643
+ type: 'string',
3644
+ description: 'Auto-generated access token'
3307
3645
  },
3308
- matchAmountTolerance: {
3646
+ role: {
3647
+ type: 'string',
3648
+ description: 'Assigned user role',
3649
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3650
+ }
3651
+ },
3652
+ required: ['authToken', 'accessToken', 'role']
3653
+ } as const;
3654
+
3655
+ export const $UpdateUserSettingDto = {
3656
+ type: 'object',
3657
+ properties: {
3658
+ secId: {
3309
3659
  type: 'number',
3310
- description: 'Amount tolerance percentage'
3660
+ description: 'Security ID'
3311
3661
  },
3312
- defaultExpenseAccount: {
3313
- type: 'object',
3314
- description: 'Default expense account'
3662
+ annualInterestRate: {
3663
+ type: 'number',
3664
+ description: 'Annual interest rate',
3665
+ example: 0.05
3315
3666
  },
3316
- defaultPaymentAccount: {
3317
- type: 'object',
3318
- description: 'Default payment account'
3667
+ currency: {
3668
+ type: 'string',
3669
+ description: 'Currency code',
3670
+ example: 'USD'
3319
3671
  },
3320
- defaultPayee: {
3321
- type: 'object',
3322
- description: 'Default payee'
3672
+ baseCurrency: {
3673
+ type: 'string',
3674
+ description: 'Base currency code',
3675
+ example: 'USD'
3323
3676
  },
3324
- isActive: {
3325
- type: 'boolean',
3326
- description: 'Whether rule is active'
3677
+ benchmark: {
3678
+ type: 'string',
3679
+ description: 'Benchmark symbol',
3680
+ example: 'SPY'
3327
3681
  },
3328
- startDate: {
3682
+ colorScheme: {
3329
3683
  type: 'string',
3330
- description: 'Rule start date (YYYY-MM-DD)'
3684
+ description: 'Color scheme',
3685
+ enum: ['DARK', 'LIGHT']
3331
3686
  },
3332
- endDate: {
3333
- type: 'object',
3334
- description: 'Rule end date (YYYY-MM-DD)'
3687
+ dateRange: {
3688
+ type: 'string',
3689
+ description: 'Date range filter',
3690
+ example: '1y'
3335
3691
  },
3336
- autoCreate: {
3337
- type: 'boolean',
3338
- description: 'Auto-create transaction on expected date'
3692
+ emergencyFund: {
3693
+ type: 'number',
3694
+ description: 'Emergency fund amount',
3695
+ example: 10000
3339
3696
  },
3340
- lastOccurrence: {
3341
- type: 'object',
3342
- description: 'Last matched occurrence date (YYYY-MM-DD)'
3697
+ 'filters.accounts': {
3698
+ description: 'Account filter IDs',
3699
+ type: 'array',
3700
+ items: {
3701
+ type: 'string'
3702
+ }
3343
3703
  },
3344
- totalCount: {
3345
- type: 'number',
3346
- description: 'Total matched transactions count'
3704
+ 'filters.assetClasses': {
3705
+ description: 'Asset class filters',
3706
+ type: 'array',
3707
+ items: {
3708
+ type: 'string'
3709
+ }
3347
3710
  },
3348
- createdAt: {
3349
- format: 'date-time',
3711
+ 'filters.dataSource': {
3350
3712
  type: 'string',
3351
- description: 'Created at timestamp'
3713
+ description: 'Data source filter'
3352
3714
  },
3353
- updatedAt: {
3354
- format: 'date-time',
3715
+ 'filters.symbol': {
3355
3716
  type: 'string',
3356
- description: 'Updated at timestamp'
3717
+ description: 'Symbol filter'
3357
3718
  },
3358
- pendingCount: {
3359
- type: 'number',
3360
- description: 'Number of pending expected transactions'
3719
+ 'filters.tags': {
3720
+ description: 'Tag filters',
3721
+ type: 'array',
3722
+ items: {
3723
+ type: 'string'
3724
+ }
3361
3725
  },
3362
- overdueCount: {
3363
- type: 'number',
3364
- description: 'Number of overdue expected transactions'
3726
+ isExperimentalFeatures: {
3727
+ type: 'boolean',
3728
+ description: 'Enable experimental features'
3365
3729
  },
3366
- nextExpectedDate: {
3367
- type: 'object',
3368
- description: 'Next expected date (YYYY-MM-DD)'
3730
+ isRestrictedView: {
3731
+ type: 'boolean',
3732
+ description: 'Enable restricted view mode'
3369
3733
  },
3370
- totalAmount: {
3371
- type: 'number',
3372
- description: 'Total amount of all matched transactions'
3734
+ language: {
3735
+ type: 'string',
3736
+ description: 'Language code',
3737
+ example: 'en'
3373
3738
  },
3374
- averageAmount: {
3375
- type: 'number',
3376
- description: 'Average amount per transaction'
3739
+ locale: {
3740
+ type: 'string',
3741
+ description: 'Locale code',
3742
+ example: 'en-US'
3377
3743
  },
3378
- transactionCount: {
3744
+ projectedTotalAmount: {
3379
3745
  type: 'number',
3380
- description: 'Number of matched transactions'
3746
+ description: 'Projected total amount',
3747
+ example: 1000000
3381
3748
  },
3382
- firstDate: {
3383
- type: 'object',
3384
- description: 'First matched transaction date (YYYY-MM-DD)'
3749
+ retirementDate: {
3750
+ type: 'string',
3751
+ description: 'Retirement date in ISO 8601 format',
3752
+ example: '2050-01-01'
3385
3753
  },
3386
- lastDate: {
3387
- type: 'object',
3388
- description: 'Last matched transaction date (YYYY-MM-DD)'
3389
- },
3390
- variance: {
3754
+ savingsRate: {
3391
3755
  type: 'number',
3392
- description: 'Amount variance (standard deviation squared)'
3756
+ description: 'Savings rate percentage',
3757
+ example: 0.2
3393
3758
  },
3394
- upcomingCount: {
3395
- type: 'number',
3396
- description: 'Number of upcoming expected transactions'
3759
+ viewMode: {
3760
+ type: 'string',
3761
+ description: 'View mode',
3762
+ enum: ['DEFAULT', 'ZEN']
3763
+ }
3764
+ }
3765
+ } as const;
3766
+
3767
+ export const $UpdatePropertyDto = {
3768
+ type: 'object',
3769
+ properties: {
3770
+ value: {
3771
+ type: 'string',
3772
+ description: 'Property value'
3397
3773
  }
3398
3774
  },
3399
- required: [
3400
- 'id',
3401
- 'userId',
3402
- 'name',
3403
- 'frequency',
3404
- 'expectedAmount',
3405
- 'currency',
3406
- 'matchAmountTolerance',
3407
- 'isActive',
3408
- 'startDate',
3409
- 'autoCreate',
3410
- 'totalCount',
3411
- 'createdAt',
3412
- 'updatedAt',
3413
- 'pendingCount',
3414
- 'overdueCount',
3415
- 'totalAmount',
3416
- 'averageAmount',
3417
- 'transactionCount',
3418
- 'variance',
3419
- 'upcomingCount'
3420
- ]
3775
+ required: ['value']
3421
3776
  } as const;
3422
3777
 
3423
- export const $UpdateRecurringRuleDto = {
3778
+ export const $CreateRecurringRuleDto = {
3424
3779
  type: 'object',
3425
3780
  properties: {
3426
3781
  name: {
3427
3782
  type: 'string',
3428
- description: 'Rule name',
3783
+ description: 'Rule name (unique per user)',
3429
3784
  maxLength: 100
3430
3785
  },
3431
3786
  icon: {
@@ -3448,7 +3803,7 @@ export const $UpdateRecurringRuleDto = {
3448
3803
  },
3449
3804
  expectedAmount: {
3450
3805
  type: 'number',
3451
- description: 'Expected amount',
3806
+ description: 'Expected amount (positive number)',
3452
3807
  minimum: 0
3453
3808
  },
3454
3809
  expectedDay: {
@@ -3459,7 +3814,7 @@ export const $UpdateRecurringRuleDto = {
3459
3814
  },
3460
3815
  customIntervalDays: {
3461
3816
  type: 'number',
3462
- description: 'Custom interval in days',
3817
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3463
3818
  minimum: 1
3464
3819
  },
3465
3820
  currency: {
@@ -3469,118 +3824,136 @@ export const $UpdateRecurringRuleDto = {
3469
3824
  },
3470
3825
  matchPayeePattern: {
3471
3826
  type: 'string',
3472
- description: 'Payee matching pattern',
3827
+ description: 'Payee matching pattern (supports wildcards)',
3473
3828
  maxLength: 200
3474
3829
  },
3475
3830
  matchAmountTolerance: {
3476
3831
  type: 'number',
3477
3832
  description: 'Amount tolerance percentage (0-1)',
3833
+ default: 0.075,
3478
3834
  minimum: 0,
3479
3835
  maximum: 1
3480
3836
  },
3481
3837
  defaultExpenseAccount: {
3482
3838
  type: 'string',
3483
- description: 'Default expense account',
3839
+ description: 'Default expense account for auto-create',
3484
3840
  maxLength: 200
3485
3841
  },
3486
3842
  defaultPaymentAccount: {
3487
3843
  type: 'string',
3488
- description: 'Default payment account',
3844
+ description: 'Default payment account for auto-create',
3489
3845
  maxLength: 200
3490
3846
  },
3491
3847
  defaultPayee: {
3492
3848
  type: 'string',
3493
- description: 'Default payee',
3849
+ description: 'Default payee for auto-create',
3494
3850
  maxLength: 200
3495
3851
  },
3496
3852
  autoCreate: {
3497
3853
  type: 'boolean',
3498
- description: 'Auto-create transaction'
3854
+ description: 'Auto-create transaction when expected date arrives',
3855
+ default: false
3499
3856
  },
3500
- isActive: {
3501
- type: 'boolean',
3502
- description: 'Rule active status'
3857
+ startDate: {
3858
+ type: 'string',
3859
+ description: 'Rule start date (ISO format)'
3503
3860
  },
3504
3861
  endDate: {
3505
3862
  type: 'string',
3506
3863
  description: 'Rule end date (ISO format)'
3507
3864
  }
3508
- }
3865
+ },
3866
+ required: [
3867
+ 'name',
3868
+ 'frequency',
3869
+ 'expectedAmount',
3870
+ 'matchAmountTolerance',
3871
+ 'autoCreate'
3872
+ ]
3509
3873
  } as const;
3510
3874
 
3511
- export const $ExpectedTransactionRuleDto = {
3875
+ export const $RecurringRuleResponseDto = {
3512
3876
  type: 'object',
3513
3877
  properties: {
3878
+ id: {
3879
+ type: 'string',
3880
+ description: 'Rule ID'
3881
+ },
3882
+ userId: {
3883
+ type: 'string',
3884
+ description: 'User ID'
3885
+ },
3514
3886
  name: {
3515
3887
  type: 'string',
3516
3888
  description: 'Rule name'
3517
3889
  },
3518
3890
  icon: {
3519
- type: 'object',
3520
- description: 'Rule icon'
3891
+ type: 'string',
3892
+ description: 'Icon emoji'
3521
3893
  },
3522
3894
  frequency: {
3523
3895
  type: 'string',
3524
- description: 'Rule frequency'
3896
+ description: 'Recurring frequency'
3897
+ },
3898
+ expectedAmount: {
3899
+ type: 'number',
3900
+ description: 'Expected amount'
3901
+ },
3902
+ expectedDay: {
3903
+ type: 'number',
3904
+ description: 'Expected day of month'
3905
+ },
3906
+ customIntervalDays: {
3907
+ type: 'number',
3908
+ description: 'Custom interval in days'
3525
3909
  },
3526
3910
  currency: {
3527
3911
  type: 'string',
3528
3912
  description: 'Currency code'
3529
- }
3530
- },
3531
- required: ['name', 'frequency', 'currency']
3532
- } as const;
3533
-
3534
- export const $ExpectedTransactionResponseDto = {
3535
- type: 'object',
3536
- properties: {
3537
- id: {
3538
- type: 'string',
3539
- description: 'Expected transaction ID'
3540
3913
  },
3541
- userId: {
3914
+ matchPayeePattern: {
3542
3915
  type: 'string',
3543
- description: 'User ID'
3916
+ description: 'Payee matching pattern'
3544
3917
  },
3545
- ruleId: {
3546
- type: 'string',
3547
- description: 'Associated rule ID'
3918
+ matchAmountTolerance: {
3919
+ type: 'number',
3920
+ description: 'Amount tolerance percentage'
3548
3921
  },
3549
- expectedDate: {
3922
+ defaultExpenseAccount: {
3550
3923
  type: 'string',
3551
- description: 'Expected date (YYYY-MM-DD)'
3924
+ description: 'Default expense account'
3552
3925
  },
3553
- expectedAmount: {
3554
- type: 'number',
3555
- description: 'Expected amount'
3926
+ defaultPaymentAccount: {
3927
+ type: 'string',
3928
+ description: 'Default payment account'
3556
3929
  },
3557
- status: {
3930
+ defaultPayee: {
3558
3931
  type: 'string',
3559
- description: 'Status (PENDING, COMPLETED, SKIPPED)'
3932
+ description: 'Default payee'
3560
3933
  },
3561
- matchedTransactionId: {
3562
- type: 'object',
3563
- description: 'Matched transaction ID'
3934
+ isActive: {
3935
+ type: 'boolean',
3936
+ description: 'Whether rule is active'
3564
3937
  },
3565
- matchedAt: {
3566
- type: 'object',
3567
- description: 'Match timestamp (ISO 8601)'
3938
+ startDate: {
3939
+ type: 'string',
3940
+ description: 'Rule start date (YYYY-MM-DD)'
3568
3941
  },
3569
- matchConfidence: {
3570
- type: 'object',
3571
- description: 'Match confidence score (0-1)'
3942
+ endDate: {
3943
+ type: 'string',
3944
+ description: 'Rule end date (YYYY-MM-DD)'
3572
3945
  },
3573
- isOverdue: {
3946
+ autoCreate: {
3574
3947
  type: 'boolean',
3575
- description: 'Whether this expected transaction is overdue'
3948
+ description: 'Auto-create transaction on expected date'
3576
3949
  },
3577
- rule: {
3578
- description: 'Rule information',
3579
- allOf: [
3580
- {
3581
- $ref: '#/components/schemas/ExpectedTransactionRuleDto'
3582
- }
3583
- ]
3950
+ lastOccurrence: {
3951
+ type: 'string',
3952
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
3953
+ },
3954
+ totalCount: {
3955
+ type: 'number',
3956
+ description: 'Total matched transactions count'
3584
3957
  },
3585
3958
  createdAt: {
3586
3959
  format: 'date-time',
@@ -3596,647 +3969,581 @@ export const $ExpectedTransactionResponseDto = {
3596
3969
  required: [
3597
3970
  'id',
3598
3971
  'userId',
3599
- 'ruleId',
3600
- 'expectedDate',
3972
+ 'name',
3973
+ 'frequency',
3601
3974
  'expectedAmount',
3602
- 'status',
3603
- 'isOverdue',
3604
- 'rule',
3975
+ 'currency',
3976
+ 'matchAmountTolerance',
3977
+ 'isActive',
3978
+ 'startDate',
3979
+ 'autoCreate',
3980
+ 'totalCount',
3605
3981
  'createdAt',
3606
3982
  'updatedAt'
3607
3983
  ]
3608
3984
  } as const;
3609
3985
 
3610
- export const $ExpectedTransactionListResponseDto = {
3986
+ export const $CreateRuleFromTransactionDto = {
3611
3987
  type: 'object',
3612
3988
  properties: {
3613
- items: {
3614
- type: 'array',
3615
- items: {
3616
- $ref: '#/components/schemas/ExpectedTransactionResponseDto'
3617
- }
3989
+ frequency: {
3990
+ type: 'string',
3991
+ description: 'Recurring frequency',
3992
+ enum: [
3993
+ 'WEEKLY',
3994
+ 'BIWEEKLY',
3995
+ 'MONTHLY',
3996
+ 'BIMONTHLY',
3997
+ 'QUARTERLY',
3998
+ 'YEARLY',
3999
+ 'CUSTOM'
4000
+ ],
4001
+ example: 'MONTHLY'
3618
4002
  },
3619
- total: {
3620
- type: 'number',
3621
- description: 'Total count'
3622
- }
3623
- },
3624
- required: ['items', 'total']
3625
- } as const;
3626
-
3627
- export const $ConfirmMatchDto = {
3628
- type: 'object',
3629
- properties: {
3630
- transactionId: {
4003
+ name: {
3631
4004
  type: 'string',
3632
- description: 'Transaction ID to match with'
4005
+ description: 'Optional name override (default: transaction payee)',
4006
+ maxLength: 100
4007
+ },
4008
+ icon: {
4009
+ type: 'string',
4010
+ description: 'Optional icon emoji',
4011
+ maxLength: 10
3633
4012
  }
3634
4013
  },
3635
- required: ['transactionId']
4014
+ required: ['frequency']
3636
4015
  } as const;
3637
4016
 
3638
- export const $EnterNowDto = {
4017
+ export const $RecurringRuleWithStatsResponseDto = {
3639
4018
  type: 'object',
3640
4019
  properties: {
3641
- expenseAccount: {
4020
+ id: {
3642
4021
  type: 'string',
3643
- description:
3644
- 'Override expense account (uses rule default if not provided)',
3645
- maxLength: 200
4022
+ description: 'Rule ID'
3646
4023
  },
3647
- paymentAccount: {
4024
+ userId: {
3648
4025
  type: 'string',
3649
- description:
3650
- 'Override payment account (uses rule default if not provided)',
3651
- maxLength: 200
4026
+ description: 'User ID'
3652
4027
  },
3653
- amount: {
3654
- type: 'number',
3655
- description: 'Override amount (uses expected amount if not provided)',
3656
- minimum: 0
4028
+ name: {
4029
+ type: 'string',
4030
+ description: 'Rule name'
3657
4031
  },
3658
- payee: {
4032
+ icon: {
3659
4033
  type: 'string',
3660
- description: 'Override payee (uses rule default if not provided)',
3661
- maxLength: 200
4034
+ description: 'Icon emoji'
3662
4035
  },
3663
- narration: {
4036
+ frequency: {
3664
4037
  type: 'string',
3665
- description: 'Optional narration',
3666
- maxLength: 500
3667
- }
3668
- }
3669
- } as const;
3670
-
3671
- export const $ForecastItemDto = {
3672
- type: 'object',
3673
- properties: {
3674
- rule: {
4038
+ description: 'Recurring frequency'
4039
+ },
4040
+ expectedAmount: {
4041
+ type: 'number',
4042
+ description: 'Expected amount'
4043
+ },
4044
+ expectedDay: {
4045
+ type: 'number',
4046
+ description: 'Expected day of month'
4047
+ },
4048
+ customIntervalDays: {
4049
+ type: 'number',
4050
+ description: 'Custom interval in days'
4051
+ },
4052
+ currency: {
3675
4053
  type: 'string',
3676
- description: 'Rule name',
3677
- example: 'Rent'
4054
+ description: 'Currency code'
3678
4055
  },
3679
- ruleId: {
4056
+ matchPayeePattern: {
3680
4057
  type: 'string',
3681
- description: 'Rule ID',
3682
- example: 'clx123...'
4058
+ description: 'Payee matching pattern'
3683
4059
  },
3684
- amount: {
4060
+ matchAmountTolerance: {
3685
4061
  type: 'number',
3686
- description: 'Expected amount',
3687
- example: 3000
4062
+ description: 'Amount tolerance percentage'
3688
4063
  },
3689
- date: {
4064
+ defaultExpenseAccount: {
3690
4065
  type: 'string',
3691
- description: 'Expected date (YYYY-MM-DD)',
3692
- example: '2024-04-01'
4066
+ description: 'Default expense account'
3693
4067
  },
3694
- icon: {
4068
+ defaultPaymentAccount: {
3695
4069
  type: 'string',
3696
- description: 'Rule icon emoji',
3697
- example: '🏠',
3698
- nullable: true
4070
+ description: 'Default payment account'
3699
4071
  },
3700
- currency: {
4072
+ defaultPayee: {
3701
4073
  type: 'string',
3702
- description: 'Currency code',
3703
- example: 'CNY'
3704
- }
3705
- },
3706
- required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
3707
- } as const;
3708
-
3709
- export const $MonthlyForecastDto = {
3710
- type: 'object',
3711
- properties: {
3712
- month: {
4074
+ description: 'Default payee'
4075
+ },
4076
+ isActive: {
4077
+ type: 'boolean',
4078
+ description: 'Whether rule is active'
4079
+ },
4080
+ startDate: {
3713
4081
  type: 'string',
3714
- description: 'Month (YYYY-MM)',
3715
- example: '2024-04'
4082
+ description: 'Rule start date (YYYY-MM-DD)'
3716
4083
  },
3717
- expectedOutflow: {
4084
+ endDate: {
4085
+ type: 'string',
4086
+ description: 'Rule end date (YYYY-MM-DD)'
4087
+ },
4088
+ autoCreate: {
4089
+ type: 'boolean',
4090
+ description: 'Auto-create transaction on expected date'
4091
+ },
4092
+ lastOccurrence: {
4093
+ type: 'string',
4094
+ description: 'Last matched occurrence date (YYYY-MM-DD)'
4095
+ },
4096
+ totalCount: {
3718
4097
  type: 'number',
3719
- description: 'Total expected outflow for the month',
3720
- example: 8500
4098
+ description: 'Total matched transactions count'
3721
4099
  },
3722
- itemCount: {
4100
+ createdAt: {
4101
+ format: 'date-time',
4102
+ type: 'string',
4103
+ description: 'Created at timestamp'
4104
+ },
4105
+ updatedAt: {
4106
+ format: 'date-time',
4107
+ type: 'string',
4108
+ description: 'Updated at timestamp'
4109
+ },
4110
+ pendingCount: {
3723
4111
  type: 'number',
3724
- description: 'Number of expected transactions',
3725
- example: 3
4112
+ description: 'Number of pending expected transactions'
3726
4113
  },
3727
- byCurrency: {
3728
- type: 'object',
3729
- description: 'Breakdown by currency',
3730
- example: {
3731
- CNY: 8500,
3732
- USD: 100
3733
- }
4114
+ overdueCount: {
4115
+ type: 'number',
4116
+ description: 'Number of overdue expected transactions'
3734
4117
  },
3735
- items: {
3736
- description: 'Individual forecast items',
3737
- type: 'array',
3738
- items: {
3739
- $ref: '#/components/schemas/ForecastItemDto'
3740
- }
3741
- }
3742
- },
3743
- required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
3744
- } as const;
3745
-
3746
- export const $ForecastResponseDto = {
3747
- type: 'object',
3748
- properties: {
3749
- forecast: {
3750
- description: 'Monthly forecast data',
3751
- type: 'array',
3752
- items: {
3753
- $ref: '#/components/schemas/MonthlyForecastDto'
3754
- }
4118
+ nextExpectedDate: {
4119
+ type: 'string',
4120
+ description: 'Next expected date (YYYY-MM-DD)'
3755
4121
  },
3756
- totalOutflow: {
4122
+ totalAmount: {
3757
4123
  type: 'number',
3758
- description: 'Total expected outflow across all months',
3759
- example: 25500
4124
+ description: 'Total amount of all matched transactions'
3760
4125
  },
3761
- totalByCurrency: {
3762
- type: 'object',
3763
- description: 'Total by currency across all months',
3764
- example: {
3765
- CNY: 25500,
3766
- USD: 300
3767
- }
4126
+ averageAmount: {
4127
+ type: 'number',
4128
+ description: 'Average amount per transaction'
3768
4129
  },
3769
- rulesCount: {
4130
+ transactionCount: {
3770
4131
  type: 'number',
3771
- description: 'Number of active recurring rules included',
3772
- example: 5
4132
+ description: 'Number of matched transactions'
3773
4133
  },
3774
- periodStart: {
4134
+ firstDate: {
3775
4135
  type: 'string',
3776
- description: 'Forecast period start date',
3777
- example: '2024-04-01'
4136
+ description: 'First matched transaction date (YYYY-MM-DD)'
3778
4137
  },
3779
- periodEnd: {
4138
+ lastDate: {
3780
4139
  type: 'string',
3781
- description: 'Forecast period end date',
3782
- example: '2024-06-30'
4140
+ description: 'Last matched transaction date (YYYY-MM-DD)'
4141
+ },
4142
+ variance: {
4143
+ type: 'number',
4144
+ description: 'Amount variance (standard deviation squared)'
4145
+ },
4146
+ upcomingCount: {
4147
+ type: 'number',
4148
+ description: 'Number of upcoming expected transactions'
3783
4149
  }
3784
4150
  },
3785
4151
  required: [
3786
- 'forecast',
3787
- 'totalOutflow',
3788
- 'totalByCurrency',
3789
- 'rulesCount',
3790
- 'periodStart',
3791
- 'periodEnd'
4152
+ 'id',
4153
+ 'userId',
4154
+ 'name',
4155
+ 'frequency',
4156
+ 'expectedAmount',
4157
+ 'currency',
4158
+ 'matchAmountTolerance',
4159
+ 'isActive',
4160
+ 'startDate',
4161
+ 'autoCreate',
4162
+ 'totalCount',
4163
+ 'createdAt',
4164
+ 'updatedAt',
4165
+ 'pendingCount',
4166
+ 'overdueCount',
4167
+ 'totalAmount',
4168
+ 'averageAmount',
4169
+ 'transactionCount',
4170
+ 'variance',
4171
+ 'upcomingCount'
3792
4172
  ]
3793
4173
  } as const;
3794
4174
 
3795
- export const $CurrencyBalanceDto = {
3796
- type: 'object',
3797
- properties: {
3798
- currency: {
3799
- type: 'string',
3800
- description: 'ISO 4217 currency code',
3801
- example: 'CNY'
3802
- },
3803
- balance: {
3804
- type: 'string',
3805
- description: 'Balance amount',
3806
- example: '500000.00'
3807
- }
3808
- },
3809
- required: ['currency', 'balance']
3810
- } as const;
3811
-
3812
- export const $TimeSeriesPointDto = {
4175
+ export const $UpdateRecurringRuleDto = {
3813
4176
  type: 'object',
3814
4177
  properties: {
3815
- date: {
3816
- type: 'string',
3817
- description: 'Date in YYYY-MM-DD format',
3818
- example: '2024-06-15'
3819
- },
3820
- value: {
4178
+ name: {
3821
4179
  type: 'string',
3822
- description: 'Value at this date (in base currency)',
3823
- example: '500000.00'
3824
- },
3825
- change: {
3826
- type: 'object',
3827
- description: 'Change from previous point',
3828
- example: '5000.00'
4180
+ description: 'Rule name (unique per user)',
4181
+ maxLength: 100
3829
4182
  },
3830
- assets: {
4183
+ icon: {
3831
4184
  type: 'string',
3832
- description: 'Total assets at this date (in base currency)',
3833
- example: '494338.00'
4185
+ description: 'Icon emoji',
4186
+ maxLength: 10
3834
4187
  },
3835
- liabilities: {
4188
+ frequency: {
3836
4189
  type: 'string',
3837
- description: 'Total liabilities at this date (in base currency)',
3838
- example: '310098.00'
4190
+ description: 'Recurring frequency',
4191
+ enum: [
4192
+ 'WEEKLY',
4193
+ 'BIWEEKLY',
4194
+ 'MONTHLY',
4195
+ 'BIMONTHLY',
4196
+ 'QUARTERLY',
4197
+ 'YEARLY',
4198
+ 'CUSTOM'
4199
+ ]
3839
4200
  },
3840
- byCurrency: {
3841
- description: 'Multi-currency breakdown for this point',
3842
- type: 'array',
3843
- items: {
3844
- $ref: '#/components/schemas/CurrencyBalanceDto'
3845
- }
3846
- }
3847
- },
3848
- required: ['date', 'value']
3849
- } as const;
3850
-
3851
- export const $TrendSummaryDto = {
3852
- type: 'object',
3853
- properties: {
3854
- startValue: {
3855
- type: 'string',
3856
- description: 'Value at start of period',
3857
- example: '450000.00'
4201
+ expectedAmount: {
4202
+ type: 'number',
4203
+ description: 'Expected amount (positive number)',
4204
+ minimum: 0
3858
4205
  },
3859
- endValue: {
3860
- type: 'string',
3861
- description: 'Value at end of period',
3862
- example: '500000.00'
4206
+ expectedDay: {
4207
+ type: 'number',
4208
+ description: 'Expected day of month (1-31)',
4209
+ minimum: 1,
4210
+ maximum: 31
3863
4211
  },
3864
- totalChange: {
4212
+ currency: {
3865
4213
  type: 'string',
3866
- description: 'Total change over period',
3867
- example: '50000.00'
4214
+ description: 'Currency code',
4215
+ maxLength: 10
3868
4216
  },
3869
- totalChangePercentage: {
3870
- type: 'string',
3871
- description: 'Total change percentage',
3872
- example: '+11.11%'
3873
- }
3874
- },
3875
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3876
- } as const;
3877
-
3878
- export const $MultiCurrencyPointDto = {
3879
- type: 'object',
3880
- properties: {
3881
- date: {
4217
+ matchPayeePattern: {
3882
4218
  type: 'string',
3883
- description: 'Date in YYYY-MM-DD format',
3884
- example: '2024-06-15'
3885
- },
3886
- byCurrency: {
3887
- description: 'Balances by currency',
3888
- type: 'array',
3889
- items: {
3890
- $ref: '#/components/schemas/CurrencyBalanceDto'
3891
- }
3892
- }
3893
- },
3894
- required: ['date', 'byCurrency']
3895
- } as const;
3896
-
3897
- export const $PortfolioTrendsResponseDto = {
3898
- type: 'object',
3899
- properties: {
3900
- series: {
3901
- description: 'Time series data points',
3902
- type: 'array',
3903
- items: {
3904
- $ref: '#/components/schemas/TimeSeriesPointDto'
3905
- }
4219
+ description: 'Payee matching pattern (supports wildcards)',
4220
+ maxLength: 200
3906
4221
  },
3907
- summary: {
3908
- description: 'Period summary',
3909
- allOf: [
3910
- {
3911
- $ref: '#/components/schemas/TrendSummaryDto'
3912
- }
3913
- ]
4222
+ matchAmountTolerance: {
4223
+ type: 'number',
4224
+ description: 'Amount tolerance percentage (0-1)',
4225
+ default: 0.075,
4226
+ minimum: 0,
4227
+ maximum: 1
3914
4228
  },
3915
- period: {
4229
+ defaultExpenseAccount: {
3916
4230
  type: 'string',
3917
- description: 'Period requested',
3918
- example: '6m'
4231
+ description: 'Default expense account for auto-create',
4232
+ maxLength: 200
3919
4233
  },
3920
- granularity: {
4234
+ defaultPaymentAccount: {
3921
4235
  type: 'string',
3922
- description: 'Data granularity',
3923
- example: 'month'
4236
+ description: 'Default payment account for auto-create',
4237
+ maxLength: 200
3924
4238
  },
3925
- currency: {
4239
+ defaultPayee: {
3926
4240
  type: 'string',
3927
- description: 'Base currency for converted values',
3928
- example: 'CNY'
4241
+ description: 'Default payee for auto-create',
4242
+ maxLength: 200
3929
4243
  },
3930
- byCurrency: {
3931
- description:
3932
- 'Multi-currency time series (each point has currency breakdown)',
3933
- type: 'array',
3934
- items: {
3935
- $ref: '#/components/schemas/MultiCurrencyPointDto'
3936
- }
4244
+ autoCreate: {
4245
+ type: 'boolean',
4246
+ description: 'Auto-create transaction when expected date arrives',
4247
+ default: false
3937
4248
  },
3938
- warnings: {
3939
- description: 'Exchange rate warnings',
3940
- type: 'array',
3941
- items: {
3942
- $ref: '#/components/schemas/ExchangeRateWarningDto'
3943
- }
4249
+ endDate: {
4250
+ type: 'string',
4251
+ description: 'Rule end date (ISO format)'
4252
+ },
4253
+ customIntervalDays: {
4254
+ type: 'number',
4255
+ description: 'Custom interval in days',
4256
+ minimum: 1
4257
+ },
4258
+ isActive: {
4259
+ type: 'boolean',
4260
+ description: 'Rule active status'
3944
4261
  }
3945
- },
3946
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4262
+ }
3947
4263
  } as const;
3948
4264
 
3949
- export const $CashFlowPointDto = {
4265
+ export const $ExpectedTransactionRuleDto = {
3950
4266
  type: 'object',
3951
4267
  properties: {
3952
- month: {
4268
+ name: {
3953
4269
  type: 'string',
3954
- description: 'Month key (YYYY-MM)',
3955
- example: '2024-03'
4270
+ description: 'Rule name'
3956
4271
  },
3957
- income: {
4272
+ icon: {
3958
4273
  type: 'string',
3959
- description: 'Income in base currency (absolute, converted)',
3960
- example: '10000.00'
4274
+ description: 'Rule icon'
3961
4275
  },
3962
- expense: {
4276
+ frequency: {
3963
4277
  type: 'string',
3964
- description: 'Expense in base currency (absolute, converted)',
3965
- example: '5000.00'
4278
+ description: 'Rule frequency'
3966
4279
  },
3967
- netSavings: {
4280
+ currency: {
3968
4281
  type: 'string',
3969
- description: 'netSavings = income − expense (savings positive)',
3970
- example: '5000.00'
4282
+ description: 'Currency code'
3971
4283
  }
3972
4284
  },
3973
- required: ['month', 'income', 'expense', 'netSavings']
4285
+ required: ['name', 'frequency', 'currency']
3974
4286
  } as const;
3975
4287
 
3976
- export const $CashFlowTrendSummaryDto = {
4288
+ export const $ExpectedTransactionResponseDto = {
3977
4289
  type: 'object',
3978
4290
  properties: {
3979
- totalIncome: {
4291
+ id: {
3980
4292
  type: 'string',
3981
- description: 'Total income across the period',
3982
- example: '60000.00'
4293
+ description: 'Expected transaction ID'
3983
4294
  },
3984
- totalExpense: {
4295
+ userId: {
3985
4296
  type: 'string',
3986
- description: 'Total expense across the period',
3987
- example: '30000.00'
4297
+ description: 'User ID'
3988
4298
  },
3989
- totalNetSavings: {
4299
+ ruleId: {
3990
4300
  type: 'string',
3991
- description: 'income − expense across the period',
3992
- example: '30000.00'
4301
+ description: 'Associated rule ID'
3993
4302
  },
3994
- averageMonthlyNetSavings: {
4303
+ expectedDate: {
3995
4304
  type: 'string',
3996
- description:
3997
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3998
- example: '5000.00'
3999
- }
4000
- },
4001
- required: [
4002
- 'totalIncome',
4003
- 'totalExpense',
4004
- 'totalNetSavings',
4005
- 'averageMonthlyNetSavings'
4006
- ]
4007
- } as const;
4008
-
4009
- export const $CashFlowTrendsResponseDto = {
4010
- type: 'object',
4011
- properties: {
4012
- series: {
4013
- description:
4014
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
4015
- type: 'array',
4016
- items: {
4017
- $ref: '#/components/schemas/CashFlowPointDto'
4018
- }
4305
+ description: 'Expected date (YYYY-MM-DD)'
4019
4306
  },
4020
- summary: {
4021
- description: 'Period totals',
4307
+ expectedAmount: {
4308
+ type: 'number',
4309
+ description: 'Expected amount'
4310
+ },
4311
+ status: {
4312
+ type: 'string',
4313
+ description: 'Status (PENDING, COMPLETED, SKIPPED)'
4314
+ },
4315
+ matchedTransactionId: {
4316
+ type: 'string',
4317
+ description: 'Matched transaction ID'
4318
+ },
4319
+ matchedAt: {
4320
+ type: 'string',
4321
+ description: 'Match timestamp (ISO 8601)'
4322
+ },
4323
+ matchConfidence: {
4324
+ type: 'number',
4325
+ description: 'Match confidence score (0-1)'
4326
+ },
4327
+ isOverdue: {
4328
+ type: 'boolean',
4329
+ description: 'Whether this expected transaction is overdue'
4330
+ },
4331
+ rule: {
4332
+ description: 'Rule information',
4022
4333
  allOf: [
4023
4334
  {
4024
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4335
+ $ref: '#/components/schemas/ExpectedTransactionRuleDto'
4025
4336
  }
4026
4337
  ]
4027
4338
  },
4028
- period: {
4029
- type: 'string',
4030
- description: 'Period requested',
4031
- example: '6m'
4032
- },
4033
- granularity: {
4339
+ createdAt: {
4340
+ format: 'date-time',
4034
4341
  type: 'string',
4035
- description: 'Data granularity (v1 returns month buckets)',
4036
- example: 'month'
4342
+ description: 'Created at timestamp'
4037
4343
  },
4038
- currency: {
4344
+ updatedAt: {
4345
+ format: 'date-time',
4039
4346
  type: 'string',
4040
- description: 'Base currency for converted values',
4041
- example: 'CNY'
4042
- },
4043
- warnings: {
4044
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
4045
- type: 'array',
4046
- items: {
4047
- $ref: '#/components/schemas/ExchangeRateWarningDto'
4048
- }
4347
+ description: 'Updated at timestamp'
4049
4348
  }
4050
4349
  },
4051
- required: ['series', 'summary', 'period', 'granularity', 'currency']
4052
- } as const;
4053
-
4054
- export const $GenerateSnapshotBody = {
4055
- type: 'object',
4056
- properties: {}
4057
- } as const;
4058
-
4059
- export const $GenerateSnapshotResponse = {
4060
- type: 'object',
4061
- properties: {}
4062
- } as const;
4063
-
4064
- export const $BackfillSnapshotsBody = {
4065
- type: 'object',
4066
- properties: {}
4067
- } as const;
4068
-
4069
- export const $BackfillSnapshotsResponse = {
4070
- type: 'object',
4071
- properties: {}
4350
+ required: [
4351
+ 'id',
4352
+ 'userId',
4353
+ 'ruleId',
4354
+ 'expectedDate',
4355
+ 'expectedAmount',
4356
+ 'status',
4357
+ 'isOverdue',
4358
+ 'rule',
4359
+ 'createdAt',
4360
+ 'updatedAt'
4361
+ ]
4072
4362
  } as const;
4073
4363
 
4074
- export const $DeleteOwnUserDto = {
4364
+ export const $ExpectedTransactionListResponseDto = {
4075
4365
  type: 'object',
4076
4366
  properties: {
4077
- accessToken: {
4078
- type: 'string',
4079
- description: 'Access token for user verification',
4080
- example: 'abc123xyz'
4367
+ items: {
4368
+ type: 'array',
4369
+ items: {
4370
+ $ref: '#/components/schemas/ExpectedTransactionResponseDto'
4371
+ }
4372
+ },
4373
+ total: {
4374
+ type: 'number',
4375
+ description: 'Total count'
4081
4376
  }
4082
4377
  },
4083
- required: ['accessToken']
4378
+ required: ['items', 'total']
4084
4379
  } as const;
4085
4380
 
4086
- export const $SignupDto = {
4381
+ export const $ConfirmMatchDto = {
4087
4382
  type: 'object',
4088
4383
  properties: {
4089
- turnstileToken: {
4384
+ transactionId: {
4090
4385
  type: 'string',
4091
- description:
4092
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4093
- example: '0.abc123def456...'
4386
+ description: 'Transaction ID to match with'
4094
4387
  }
4095
- }
4388
+ },
4389
+ required: ['transactionId']
4096
4390
  } as const;
4097
4391
 
4098
- export const $SignupResponseDto = {
4392
+ export const $EnterNowDto = {
4099
4393
  type: 'object',
4100
4394
  properties: {
4101
- authToken: {
4395
+ expenseAccount: {
4102
4396
  type: 'string',
4103
- description: 'JWT auth token',
4104
- example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4397
+ description:
4398
+ 'Override expense account (uses rule default if not provided)',
4399
+ maxLength: 200
4105
4400
  },
4106
- accessToken: {
4401
+ paymentAccount: {
4402
+ type: 'string',
4403
+ description:
4404
+ 'Override payment account (uses rule default if not provided)',
4405
+ maxLength: 200
4406
+ },
4407
+ amount: {
4408
+ type: 'number',
4409
+ description: 'Override amount (uses expected amount if not provided)',
4410
+ minimum: 0
4411
+ },
4412
+ payee: {
4107
4413
  type: 'string',
4108
- description: 'Auto-generated access token'
4414
+ description: 'Override payee (uses rule default if not provided)',
4415
+ maxLength: 200
4109
4416
  },
4110
- role: {
4417
+ narration: {
4111
4418
  type: 'string',
4112
- description: 'Assigned user role',
4113
- enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4419
+ description: 'Optional narration',
4420
+ maxLength: 500
4114
4421
  }
4115
- },
4116
- required: ['authToken', 'accessToken', 'role']
4422
+ }
4117
4423
  } as const;
4118
4424
 
4119
- export const $UpdateUserSettingDto = {
4425
+ export const $ForecastItemDto = {
4120
4426
  type: 'object',
4121
4427
  properties: {
4122
- secId: {
4123
- type: 'number',
4124
- description: 'Security ID'
4428
+ rule: {
4429
+ type: 'string',
4430
+ description: 'Rule name',
4431
+ example: 'Rent'
4125
4432
  },
4126
- annualInterestRate: {
4433
+ ruleId: {
4434
+ type: 'string',
4435
+ description: 'Rule ID',
4436
+ example: 'clx123...'
4437
+ },
4438
+ amount: {
4127
4439
  type: 'number',
4128
- description: 'Annual interest rate',
4129
- example: 0.05
4440
+ description: 'Expected amount',
4441
+ example: 3000
4130
4442
  },
4131
- currency: {
4443
+ date: {
4132
4444
  type: 'string',
4133
- description: 'Currency code',
4134
- example: 'USD'
4445
+ description: 'Expected date (YYYY-MM-DD)',
4446
+ example: '2024-04-01'
4135
4447
  },
4136
- baseCurrency: {
4448
+ icon: {
4137
4449
  type: 'string',
4138
- description: 'Base currency code',
4139
- example: 'USD'
4450
+ description: 'Rule icon emoji',
4451
+ example: '🏠',
4452
+ nullable: true
4140
4453
  },
4141
- benchmark: {
4454
+ currency: {
4142
4455
  type: 'string',
4143
- description: 'Benchmark symbol',
4144
- example: 'SPY'
4145
- },
4146
- colorScheme: {
4456
+ description: 'Currency code',
4457
+ example: 'CNY'
4458
+ }
4459
+ },
4460
+ required: ['rule', 'ruleId', 'amount', 'date', 'icon', 'currency']
4461
+ } as const;
4462
+
4463
+ export const $MonthlyForecastDto = {
4464
+ type: 'object',
4465
+ properties: {
4466
+ month: {
4147
4467
  type: 'string',
4148
- description: 'Color scheme',
4149
- enum: ['DARK', 'LIGHT']
4468
+ description: 'Month (YYYY-MM)',
4469
+ example: '2024-04'
4150
4470
  },
4151
- dateRange: {
4152
- type: 'string',
4153
- description: 'Date range filter',
4154
- example: '1y'
4471
+ expectedOutflow: {
4472
+ type: 'number',
4473
+ description: 'Total expected outflow for the month',
4474
+ example: 8500
4155
4475
  },
4156
- emergencyFund: {
4476
+ itemCount: {
4157
4477
  type: 'number',
4158
- description: 'Emergency fund amount',
4159
- example: 10000
4478
+ description: 'Number of expected transactions',
4479
+ example: 3
4160
4480
  },
4161
- 'filters.accounts': {
4162
- description: 'Account filter IDs',
4163
- type: 'array',
4164
- items: {
4165
- type: 'string'
4481
+ byCurrency: {
4482
+ type: 'object',
4483
+ description: 'Breakdown by currency',
4484
+ example: {
4485
+ CNY: 8500,
4486
+ USD: 100
4166
4487
  }
4167
4488
  },
4168
- 'filters.assetClasses': {
4169
- description: 'Asset class filters',
4489
+ items: {
4490
+ description: 'Individual forecast items',
4170
4491
  type: 'array',
4171
4492
  items: {
4172
- type: 'string'
4493
+ $ref: '#/components/schemas/ForecastItemDto'
4173
4494
  }
4174
- },
4175
- 'filters.dataSource': {
4176
- type: 'string',
4177
- description: 'Data source filter'
4178
- },
4179
- 'filters.symbol': {
4180
- type: 'string',
4181
- description: 'Symbol filter'
4182
- },
4183
- 'filters.tags': {
4184
- description: 'Tag filters',
4495
+ }
4496
+ },
4497
+ required: ['month', 'expectedOutflow', 'itemCount', 'byCurrency', 'items']
4498
+ } as const;
4499
+
4500
+ export const $ForecastResponseDto = {
4501
+ type: 'object',
4502
+ properties: {
4503
+ forecast: {
4504
+ description: 'Monthly forecast data',
4185
4505
  type: 'array',
4186
4506
  items: {
4187
- type: 'string'
4507
+ $ref: '#/components/schemas/MonthlyForecastDto'
4188
4508
  }
4189
4509
  },
4190
- isExperimentalFeatures: {
4191
- type: 'boolean',
4192
- description: 'Enable experimental features'
4193
- },
4194
- isRestrictedView: {
4195
- type: 'boolean',
4196
- description: 'Enable restricted view mode'
4197
- },
4198
- language: {
4199
- type: 'string',
4200
- description: 'Language code',
4201
- example: 'en'
4202
- },
4203
- locale: {
4204
- type: 'string',
4205
- description: 'Locale code',
4206
- example: 'en-US'
4207
- },
4208
- projectedTotalAmount: {
4510
+ totalOutflow: {
4209
4511
  type: 'number',
4210
- description: 'Projected total amount',
4211
- example: 1000000
4512
+ description: 'Total expected outflow across all months',
4513
+ example: 25500
4212
4514
  },
4213
- retirementDate: {
4214
- type: 'string',
4215
- description: 'Retirement date in ISO 8601 format',
4216
- example: '2050-01-01'
4515
+ totalByCurrency: {
4516
+ type: 'object',
4517
+ description: 'Total by currency across all months',
4518
+ example: {
4519
+ CNY: 25500,
4520
+ USD: 300
4521
+ }
4217
4522
  },
4218
- savingsRate: {
4523
+ rulesCount: {
4219
4524
  type: 'number',
4220
- description: 'Savings rate percentage',
4221
- example: 0.2
4525
+ description: 'Number of active recurring rules included',
4526
+ example: 5
4222
4527
  },
4223
- viewMode: {
4528
+ periodStart: {
4224
4529
  type: 'string',
4225
- description: 'View mode',
4226
- enum: ['DEFAULT', 'ZEN']
4227
- }
4228
- }
4229
- } as const;
4230
-
4231
- export const $UpdatePropertyDto = {
4232
- type: 'object',
4233
- properties: {
4234
- value: {
4530
+ description: 'Forecast period start date',
4531
+ example: '2024-04-01'
4532
+ },
4533
+ periodEnd: {
4235
4534
  type: 'string',
4236
- description: 'Property value'
4535
+ description: 'Forecast period end date',
4536
+ example: '2024-06-30'
4237
4537
  }
4238
4538
  },
4239
- required: ['value']
4539
+ required: [
4540
+ 'forecast',
4541
+ 'totalOutflow',
4542
+ 'totalByCurrency',
4543
+ 'rulesCount',
4544
+ 'periodStart',
4545
+ 'periodEnd'
4546
+ ]
4240
4547
  } as const;
4241
4548
 
4242
4549
  export const $CreateTransactionRuleDto = {
@@ -4798,7 +5105,8 @@ export const $UpdateTransactionRuleDto = {
4798
5105
  },
4799
5106
  matchLogic: {
4800
5107
  type: 'string',
4801
- enum: ['OR', 'AND']
5108
+ enum: ['OR', 'AND'],
5109
+ default: 'OR'
4802
5110
  },
4803
5111
  amountMin: {
4804
5112
  type: 'number',
@@ -4812,13 +5120,10 @@ export const $UpdateTransactionRuleDto = {
4812
5120
  },
4813
5121
  priority: {
4814
5122
  type: 'number',
5123
+ default: 50,
4815
5124
  minimum: 0,
4816
5125
  maximum: 1000
4817
5126
  },
4818
- enabled: {
4819
- type: 'boolean',
4820
- description: 'Enable or disable the rule'
4821
- },
4822
5127
  additionalTags: {
4823
5128
  items: {
4824
5129
  type: 'array'
@@ -4828,6 +5133,10 @@ export const $UpdateTransactionRuleDto = {
4828
5133
  },
4829
5134
  additionalMetadata: {
4830
5135
  type: 'object'
5136
+ },
5137
+ enabled: {
5138
+ type: 'boolean',
5139
+ description: 'Enable or disable the rule'
4831
5140
  }
4832
5141
  }
4833
5142
  } as const;
@@ -4856,36 +5165,113 @@ export const $TestRuleDto = {
4856
5165
  maxLength: 10
4857
5166
  }
4858
5167
  },
4859
- required: ['narration']
5168
+ required: ['narration']
5169
+ } as const;
5170
+
5171
+ export const $TestRuleResponseDto = {
5172
+ type: 'object',
5173
+ properties: {
5174
+ ruleId: {
5175
+ type: 'string',
5176
+ description: 'Rule ID that was tested'
5177
+ },
5178
+ matches: {
5179
+ type: 'boolean',
5180
+ description: 'Whether the rule matched the test data'
5181
+ },
5182
+ confidence: {
5183
+ type: 'number',
5184
+ description: 'Match confidence score (0-1)',
5185
+ example: 0.85
5186
+ },
5187
+ matchDetails: {
5188
+ type: 'object',
5189
+ description: 'Details of which fields matched',
5190
+ example: {
5191
+ narration: true,
5192
+ payee: false,
5193
+ categoryAccount: false
5194
+ }
5195
+ }
5196
+ },
5197
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5198
+ } as const;
5199
+
5200
+ export const $CategoryCatalogEntryDto = {
5201
+ type: 'object',
5202
+ properties: {
5203
+ slug: {
5204
+ type: 'string',
5205
+ description: 'Category slug (single source-of-truth)',
5206
+ example: 'food'
5207
+ },
5208
+ scenario: {
5209
+ type: 'string',
5210
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5211
+ enum: [
5212
+ 'expense',
5213
+ 'income',
5214
+ 'investment',
5215
+ 'banking',
5216
+ 'transfer',
5217
+ 'payment'
5218
+ ],
5219
+ example: 'expense'
5220
+ },
5221
+ icon: {
5222
+ type: 'string',
5223
+ description: 'Lucide icon name',
5224
+ example: 'utensils'
5225
+ },
5226
+ regions: {
5227
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5228
+ example: ['*'],
5229
+ type: 'array',
5230
+ items: {
5231
+ type: 'string'
5232
+ }
5233
+ },
5234
+ categoryAccounts: {
5235
+ description:
5236
+ "Beancount account paths (categoryAccount) of the region-enabled system rules whose categoryKeywords include this slug (#816). System rules only (public endpoint — user rules excluded); one-to-many by design (e.g. 'utilities' → Electricity/Water/Internet/Gas), sorted, [] when no rule maps the slug.",
5237
+ example: [
5238
+ 'Expenses:Utilities:Electricity',
5239
+ 'Expenses:Utilities:Gas',
5240
+ 'Expenses:Utilities:Internet',
5241
+ 'Expenses:Utilities:Water'
5242
+ ],
5243
+ type: 'array',
5244
+ items: {
5245
+ type: 'string'
5246
+ }
5247
+ }
5248
+ },
5249
+ required: ['slug', 'scenario', 'icon', 'regions', 'categoryAccounts']
4860
5250
  } as const;
4861
5251
 
4862
- export const $TestRuleResponseDto = {
5252
+ export const $CategoryCatalogListResponseDto = {
4863
5253
  type: 'object',
4864
5254
  properties: {
4865
- ruleId: {
4866
- type: 'string',
4867
- description: 'Rule ID that was tested'
4868
- },
4869
- matches: {
4870
- type: 'boolean',
4871
- description: 'Whether the rule matched the test data'
5255
+ items: {
5256
+ description: 'Category entries (region-scoped, query-filtered)',
5257
+ type: 'array',
5258
+ items: {
5259
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5260
+ }
4872
5261
  },
4873
- confidence: {
5262
+ total: {
4874
5263
  type: 'number',
4875
- description: 'Match confidence score (0-1)',
4876
- example: 0.85
5264
+ description:
5265
+ 'Total category entries for the region (before query filtering)',
5266
+ example: 30
4877
5267
  },
4878
- matchDetails: {
4879
- type: 'object',
4880
- description: 'Details of which fields matched',
4881
- example: {
4882
- narration: true,
4883
- payee: false,
4884
- categoryAccount: false
4885
- }
5268
+ region: {
5269
+ type: 'string',
5270
+ description: 'Region code',
5271
+ example: 'cn'
4886
5272
  }
4887
5273
  },
4888
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5274
+ required: ['items', 'total', 'region']
4889
5275
  } as const;
4890
5276
 
4891
5277
  export const $CreateBeanEventDto = {
@@ -5051,6 +5437,14 @@ export const $OnboardingAccountDto = {
5051
5437
  description:
5052
5438
  'Platform ID to bind the account to (references Platform.id); omit for unbound',
5053
5439
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5440
+ },
5441
+ displayName: {
5442
+ type: 'string',
5443
+ description:
5444
+ 'User-set display name override (omit/null = keep the derived name)',
5445
+ nullable: true,
5446
+ maxLength: 50,
5447
+ example: 'Salary card'
5054
5448
  }
5055
5449
  },
5056
5450
  required: ['path', 'currency']
@@ -5630,7 +6024,8 @@ export const $UpdateMapperDefaultsDto = {
5630
6024
  type: 'string',
5631
6025
  description: 'Source account for transactions (Beancount format)',
5632
6026
  example: 'Assets:CN:Alipay:Balance',
5633
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6027
+ pattern:
6028
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5634
6029
  },
5635
6030
  currency: {
5636
6031
  type: 'string',
@@ -5644,13 +6039,15 @@ export const $UpdateMapperDefaultsDto = {
5644
6039
  type: 'string',
5645
6040
  description: 'Default expense account (optional)',
5646
6041
  example: 'Expenses:Unknown',
5647
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6042
+ pattern:
6043
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5648
6044
  },
5649
6045
  incomeAccount: {
5650
6046
  type: 'string',
5651
6047
  description: 'Default income account (optional)',
5652
6048
  example: 'Income:Unknown',
5653
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6049
+ pattern:
6050
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
5654
6051
  },
5655
6052
  methodAccountMapping: {
5656
6053
  type: 'object',
@@ -5707,12 +6104,14 @@ export const $ProviderSyncConfigDto = {
5707
6104
  },
5708
6105
  defaultExpenseAccount: {
5709
6106
  type: 'string',
5710
- description: 'Default expense account for the second posting',
6107
+ description:
6108
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5711
6109
  example: 'Expenses:Unknown'
5712
6110
  },
5713
6111
  defaultIncomeAccount: {
5714
6112
  type: 'string',
5715
- description: 'Default income account for the second posting',
6113
+ description:
6114
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
5716
6115
  example: 'Income:Unknown'
5717
6116
  },
5718
6117
  filterPending: {
@@ -5727,12 +6126,7 @@ export const $ProviderSyncConfigDto = {
5727
6126
  example: 'acc_gocardless_001'
5728
6127
  }
5729
6128
  },
5730
- required: [
5731
- 'sourceAccount',
5732
- 'defaultCurrency',
5733
- 'defaultExpenseAccount',
5734
- 'defaultIncomeAccount'
5735
- ]
6129
+ required: ['sourceAccount', 'defaultCurrency']
5736
6130
  } as const;
5737
6131
 
5738
6132
  export const $ProviderSyncDto = {
@@ -5942,15 +6336,106 @@ export const $UncoveredFormatMissDto = {
5942
6336
  properties: {}
5943
6337
  } as const;
5944
6338
 
6339
+ export const $ClientParsedDataDto = {
6340
+ type: 'object',
6341
+ properties: {
6342
+ amount: {
6343
+ type: 'number',
6344
+ description: 'Transaction amount',
6345
+ example: 35
6346
+ },
6347
+ currency: {
6348
+ type: 'string',
6349
+ description: 'Currency code',
6350
+ example: 'CNY'
6351
+ },
6352
+ date: {
6353
+ type: 'string',
6354
+ description: 'Transaction date (ISO 8601)',
6355
+ example: '2026-08-15'
6356
+ },
6357
+ payee: {
6358
+ type: 'string',
6359
+ description: 'Payee/merchant name',
6360
+ example: 'Starbucks'
6361
+ },
6362
+ narration: {
6363
+ type: 'string',
6364
+ description: 'Transaction narration'
6365
+ },
6366
+ category: {
6367
+ type: 'string',
6368
+ description:
6369
+ 'Category in canonical form (catalog slug or Stage-2 rule keyword token, ADR-0116) — echo back verbatim from parsedData.category. Foreign forms (locale display names, account paths) are rejected with nlp.category.invalid.',
6370
+ example: 'food'
6371
+ },
6372
+ incomeType: {
6373
+ type: 'string',
6374
+ description: 'Income type',
6375
+ example: 'Salary'
6376
+ },
6377
+ incomeSource: {
6378
+ type: 'string',
6379
+ description: 'Income source',
6380
+ example: 'Anthropic Inc.'
6381
+ },
6382
+ symbol: {
6383
+ type: 'string',
6384
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6385
+ example: 'AAPL'
6386
+ },
6387
+ quantity: {
6388
+ type: 'number',
6389
+ description: 'Quantity of shares/units',
6390
+ example: 100
6391
+ },
6392
+ price: {
6393
+ type: 'number',
6394
+ description: 'Unit price per share/unit',
6395
+ example: 1900
6396
+ },
6397
+ investmentAction: {
6398
+ type: 'string',
6399
+ description: 'Investment action',
6400
+ enum: ['buy', 'sell'],
6401
+ example: 'buy'
6402
+ },
6403
+ paymentSource: {
6404
+ type: 'string',
6405
+ description: 'Payment source: asset (default) or liability (credit card)',
6406
+ enum: ['asset', 'liability'],
6407
+ example: 'asset'
6408
+ },
6409
+ liabilityHint: {
6410
+ type: 'string',
6411
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6412
+ example: 'CreditCard'
6413
+ },
6414
+ warning: {
6415
+ type: 'string',
6416
+ description:
6417
+ 'Display-only warning from the prior response; accepted but ignored.',
6418
+ example: 'Cross-currency settlement applies.'
6419
+ }
6420
+ }
6421
+ } as const;
6422
+
5945
6423
  export const $ProcessNlpDto = {
5946
6424
  type: 'object',
5947
6425
  properties: {
5948
6426
  message: {
5949
6427
  type: 'string',
5950
- description: 'Natural language text describing a transaction (Chinese)',
5951
- example: 'yesterday Starbucks spent 35 yuan',
6428
+ description:
6429
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6430
+ example: 'Starbucks 35',
5952
6431
  maxLength: 500
5953
6432
  },
6433
+ confirm: {
6434
+ type: 'boolean',
6435
+ description:
6436
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6437
+ example: true
6438
+ },
5954
6439
  sessionId: {
5955
6440
  type: 'string',
5956
6441
  description:
@@ -5958,17 +6443,51 @@ export const $ProcessNlpDto = {
5958
6443
  example: 'session_abc123'
5959
6444
  },
5960
6445
  parsedData: {
5961
- type: 'object',
5962
6446
  description:
5963
6447
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
5964
6448
  example: {
5965
6449
  amount: 35,
5966
6450
  currency: 'CNY',
5967
6451
  payee: 'Starbucks'
5968
- }
6452
+ },
6453
+ allOf: [
6454
+ {
6455
+ $ref: '#/components/schemas/ClientParsedDataDto'
6456
+ }
6457
+ ]
6458
+ },
6459
+ selectedRuleId: {
6460
+ type: 'string',
6461
+ description:
6462
+ 'confirm_rule echo-back: rule id selected from the prior confirm_rule response (matchedRule.id or alternatives[i].ruleId). Applied directly when the session is confirming_rule — no NL re-parse.',
6463
+ example: 'rule_abc123'
6464
+ },
6465
+ selectedAccount: {
6466
+ type: 'string',
6467
+ description:
6468
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i].path, or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6469
+ example: 'Expenses:Food:Coffee'
6470
+ },
6471
+ viewpointAccount: {
6472
+ type: 'string',
6473
+ description:
6474
+ 'Viewpoint account hint: the beancount path the user drilled into (e.g. from an account drill-down). Tie-break only — never overrides accounts resolved from the text. Must be an owned, OPEN Assets:/Liabilities: account; unresolvable hints are silently ignored.',
6475
+ example: 'Assets:CN:Bank:ICBC'
6476
+ },
6477
+ viewpointCategory: {
6478
+ type: 'string',
6479
+ description:
6480
+ "Viewpoint category hint: the ADR-0075 Group segment the user drilled into (e.g. 'Food'). Resolved to a concrete OPEN account in that group; tie-break only — never overrides a category resolved from the text.",
6481
+ example: 'Food'
6482
+ },
6483
+ viewpointFlow: {
6484
+ type: 'string',
6485
+ description:
6486
+ "Companion flow root for viewpointCategory ('income' | 'expense'), mirroring the ADR-0126 list-endpoint invariant. Derived from the session's routed intent (multi-turn) when absent; a first-turn flow-less category hint is dropped — send the flow explicitly.",
6487
+ enum: ['income', 'expense'],
6488
+ example: 'expense'
5969
6489
  }
5970
- },
5971
- required: ['message']
6490
+ }
5972
6491
  } as const;
5973
6492
 
5974
6493
  export const $NlpTransactionInfoDto = {
@@ -6034,7 +6553,9 @@ export const $NlpParsedDataDto = {
6034
6553
  },
6035
6554
  category: {
6036
6555
  type: 'string',
6037
- description: 'Category'
6556
+ description:
6557
+ 'Category in canonical form: a CATEGORY_CATALOG slug (GET /{region}/bean/categories) or a Stage-2 rule categoryKeywords token (ADR-0116 D2). Never a locale display name or a beancount account path. Echo back verbatim on confirm.',
6558
+ example: 'food'
6038
6559
  },
6039
6560
  incomeType: {
6040
6561
  type: 'string',
@@ -6280,6 +6801,24 @@ export const $NlpRuleConfirmationDataDto = {
6280
6801
  ]
6281
6802
  } as const;
6282
6803
 
6804
+ export const $NlpAccountCandidateDto = {
6805
+ type: 'object',
6806
+ properties: {
6807
+ path: {
6808
+ type: 'string',
6809
+ description: 'Canonical beancount account path (echo back on selection)',
6810
+ example: 'Expenses:Food:Dining'
6811
+ },
6812
+ name: {
6813
+ type: 'string',
6814
+ description:
6815
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6816
+ example: '餐饮'
6817
+ }
6818
+ },
6819
+ required: ['path', 'name']
6820
+ } as const;
6821
+
6283
6822
  export const $NlpAccountConfirmationDataDto = {
6284
6823
  type: 'object',
6285
6824
  properties: {
@@ -6295,10 +6834,11 @@ export const $NlpAccountConfirmationDataDto = {
6295
6834
  example: 'Expenses:Food:Drinks'
6296
6835
  },
6297
6836
  similarAccounts: {
6298
- description: 'Similar accounts for user selection',
6837
+ description:
6838
+ 'Similar accounts for user selection (path + localized name, #680)',
6299
6839
  type: 'array',
6300
6840
  items: {
6301
- type: 'string'
6841
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
6302
6842
  }
6303
6843
  },
6304
6844
  errorMessage: {
@@ -6485,7 +7025,8 @@ export const $NlpSuggestedAccountDto = {
6485
7025
  },
6486
7026
  confidence: {
6487
7027
  type: 'number',
6488
- description: 'Confidence score for this suggestion (0-1)',
7028
+ description:
7029
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
6489
7030
  example: 0.9
6490
7031
  }
6491
7032
  },
@@ -6521,23 +7062,31 @@ export const $NlpDefaultAccountsDto = {
6521
7062
  properties: {
6522
7063
  asset: {
6523
7064
  type: 'string',
6524
- description: 'Default asset account',
6525
- example: 'Assets:Checking'
7065
+ description:
7066
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7067
+ example: 'Assets:Checking',
7068
+ nullable: true
6526
7069
  },
6527
7070
  expense: {
6528
7071
  type: 'string',
6529
- description: 'Default expense account',
6530
- example: 'Expenses:Uncategorized'
7072
+ description:
7073
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7074
+ example: 'Expenses:Food:Coffee',
7075
+ nullable: true
6531
7076
  },
6532
7077
  income: {
6533
7078
  type: 'string',
6534
- description: 'Default income account',
6535
- example: 'Income:Uncategorized'
7079
+ description:
7080
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7081
+ example: 'Income:Salary',
7082
+ nullable: true
6536
7083
  },
6537
7084
  liability: {
6538
7085
  type: 'string',
6539
- description: 'Default liability account',
6540
- example: 'Liabilities:CreditCard'
7086
+ description:
7087
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7088
+ example: 'Liabilities:CreditCard',
7089
+ nullable: true
6541
7090
  }
6542
7091
  },
6543
7092
  required: ['asset', 'expense', 'income', 'liability']
@@ -6577,7 +7126,7 @@ export const $NlpResponseDto = {
6577
7126
  type: 'string',
6578
7127
  description:
6579
7128
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
6580
- enum: ['transfer', 'banking', 'investment'],
7129
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
6581
7130
  example: 'investment'
6582
7131
  },
6583
7132
  liabilitySubType: {
@@ -6725,7 +7274,7 @@ export const $NlpResponseDto = {
6725
7274
  },
6726
7275
  suggestedAccounts: {
6727
7276
  description:
6728
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
7277
+ 'Suggested accounts for this transaction (#586). confirm/confirm_rule/confirm_account: predicted (source/destination carry confidence); created: actual persisted accounts (confidence omitted). confirm_account destination is the suggested replacement, never the invalid account.',
6729
7278
  allOf: [
6730
7279
  {
6731
7280
  $ref: '#/components/schemas/NlpSuggestedAccountsDto'
@@ -6734,7 +7283,7 @@ export const $NlpResponseDto = {
6734
7283
  },
6735
7284
  defaultAccounts: {
6736
7285
  description:
6737
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
7286
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
6738
7287
  allOf: [
6739
7288
  {
6740
7289
  $ref: '#/components/schemas/NlpDefaultAccountsDto'
@@ -6780,13 +7329,26 @@ export const $PlatformListItemDto = {
6780
7329
  suggestedSegment: {
6781
7330
  type: 'string',
6782
7331
  description:
6783
- 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
7332
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6784
7333
  },
6785
7334
  logoUrl: {
6786
7335
  type: 'string',
6787
7336
  description: 'Logo URL',
6788
7337
  nullable: true
6789
7338
  },
7339
+ countryCode: {
7340
+ type: 'string',
7341
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7342
+ example: 'CN',
7343
+ nullable: true
7344
+ },
7345
+ category: {
7346
+ type: 'string',
7347
+ description:
7348
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7349
+ nullable: true,
7350
+ example: 'DigitalWallet'
7351
+ },
6790
7352
  isBound: {
6791
7353
  type: 'boolean',
6792
7354
  description: 'Whether user has accounts using this platform'
@@ -6800,6 +7362,8 @@ export const $PlatformListItemDto = {
6800
7362
  'canonical',
6801
7363
  'suggestedSegment',
6802
7364
  'logoUrl',
7365
+ 'countryCode',
7366
+ 'category',
6803
7367
  'isBound'
6804
7368
  ]
6805
7369
  } as const;
@@ -6835,13 +7399,26 @@ export const $PlatformMatchResultDto = {
6835
7399
  suggestedSegment: {
6836
7400
  type: 'string',
6837
7401
  description:
6838
- 'Suggested path segment — canonical, already in ACCOUNT_RE format'
7402
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
6839
7403
  },
6840
7404
  logoUrl: {
6841
7405
  type: 'string',
6842
7406
  description: 'Logo URL',
6843
7407
  nullable: true
6844
7408
  },
7409
+ countryCode: {
7410
+ type: 'string',
7411
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7412
+ example: 'CN',
7413
+ nullable: true
7414
+ },
7415
+ category: {
7416
+ type: 'string',
7417
+ description:
7418
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7419
+ nullable: true,
7420
+ example: 'DigitalWallet'
7421
+ },
6845
7422
  matchType: {
6846
7423
  type: 'string',
6847
7424
  description: "How this row matched: 'exact' > 'prefix' > 'substring'",
@@ -6855,6 +7432,8 @@ export const $PlatformMatchResultDto = {
6855
7432
  'type',
6856
7433
  'suggestedSegment',
6857
7434
  'logoUrl',
7435
+ 'countryCode',
7436
+ 'category',
6858
7437
  'matchType'
6859
7438
  ]
6860
7439
  } as const;
@@ -6884,7 +7463,80 @@ export const $PlatformMatchResponseDto = {
6884
7463
  description: 'true when total > platforms.length (more matches exist)'
6885
7464
  }
6886
7465
  },
6887
- required: ['platforms', 'matchType', 'total', 'hasMore']
7466
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7467
+ } as const;
7468
+
7469
+ export const $PlatformStandardsPlatformDto = {
7470
+ type: 'object',
7471
+ properties: {
7472
+ id: {
7473
+ type: 'string',
7474
+ description: 'Global platform ID'
7475
+ },
7476
+ name: {
7477
+ type: 'string',
7478
+ description: 'Platform name (e.g., "ICBC")'
7479
+ },
7480
+ canonical: {
7481
+ type: 'string',
7482
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7483
+ },
7484
+ suggestedSegment: {
7485
+ type: 'string',
7486
+ description:
7487
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7488
+ },
7489
+ type: {
7490
+ type: 'string',
7491
+ description: 'Platform type',
7492
+ enum: [
7493
+ 'BANK',
7494
+ 'BROKERAGE',
7495
+ 'CRYPTO_EXCHANGE',
7496
+ 'PAYMENT',
7497
+ 'INVESTMENT',
7498
+ 'INSURANCE',
7499
+ 'OTHER'
7500
+ ]
7501
+ },
7502
+ category: {
7503
+ type: 'string',
7504
+ description:
7505
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7506
+ nullable: true,
7507
+ example: 'Bank'
7508
+ }
7509
+ },
7510
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
7511
+ } as const;
7512
+
7513
+ export const $PlatformStandardsResponseDto = {
7514
+ type: 'object',
7515
+ properties: {
7516
+ platform: {
7517
+ description: 'The selected platform (institution lock source)',
7518
+ allOf: [
7519
+ {
7520
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
7521
+ }
7522
+ ]
7523
+ },
7524
+ region: {
7525
+ type: 'string',
7526
+ description:
7527
+ "Resolved template region (ISO 3166-1 alpha-2, UPPERCASE): the platform's own countryCode when set, else the region query param. For regions without a regional template file the template list falls back to the universal-only catalog while region still echoes the code.",
7528
+ example: 'CN'
7529
+ },
7530
+ templates: {
7531
+ description:
7532
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7533
+ type: 'array',
7534
+ items: {
7535
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7536
+ }
7537
+ }
7538
+ },
7539
+ required: ['platform', 'region', 'templates']
6888
7540
  } as const;
6889
7541
 
6890
7542
  export const $CreatePlatformDto = {
@@ -6988,7 +7640,8 @@ export const $UpdatePlatformDto = {
6988
7640
  },
6989
7641
  isActive: {
6990
7642
  type: 'boolean',
6991
- description: 'Whether the platform is active'
7643
+ description: 'Whether the platform is active',
7644
+ default: true
6992
7645
  }
6993
7646
  }
6994
7647
  } as const;
@@ -7151,7 +7804,8 @@ export const $AccountItemDto = {
7151
7804
  },
7152
7805
  displayName: {
7153
7806
  type: 'string',
7154
- description: 'Display name (last part of account path)',
7807
+ description:
7808
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7155
7809
  example: 'Savings'
7156
7810
  },
7157
7811
  balance: {
@@ -7187,7 +7841,8 @@ export const $PlatformGroupDto = {
7187
7841
  example: 'CMB Bank'
7188
7842
  },
7189
7843
  accounts: {
7190
- description: 'Accounts within this platform',
7844
+ description:
7845
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7191
7846
  type: 'array',
7192
7847
  items: {
7193
7848
  $ref: '#/components/schemas/AccountItemDto'
@@ -7195,7 +7850,8 @@ export const $PlatformGroupDto = {
7195
7850
  },
7196
7851
  totalBalance: {
7197
7852
  type: 'string',
7198
- description: 'FX-converted total balance in base currency',
7853
+ description:
7854
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7199
7855
  example: '100000.00'
7200
7856
  },
7201
7857
  balanceByCurrency: {
@@ -7214,7 +7870,7 @@ export const $PlatformGroupDto = {
7214
7870
  sharePct: {
7215
7871
  type: 'number',
7216
7872
  description:
7217
- 'Share of the grand converted total (0-100); 0 when grand total is 0',
7873
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7218
7874
  example: 42.5
7219
7875
  }
7220
7876
  },
@@ -7262,7 +7918,8 @@ export const $AccountsSummaryDto = {
7262
7918
  properties: {
7263
7919
  totalAccounts: {
7264
7920
  type: 'number',
7265
- description: 'Total number of accounts'
7921
+ description:
7922
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
7266
7923
  },
7267
7924
  totalPlatforms: {
7268
7925
  type: 'number',
@@ -7320,7 +7977,8 @@ export const $AccountItemWithAssetClassDto = {
7320
7977
  },
7321
7978
  displayName: {
7322
7979
  type: 'string',
7323
- description: 'Display name (last part of account path)',
7980
+ description:
7981
+ 'Display name: user-set name if provided (#762), else the ADR-0114 chain — request-locale catalog name, en pivot, then the last part of the account path (#771)',
7324
7982
  example: 'Savings'
7325
7983
  },
7326
7984
  balance: {
@@ -7806,7 +8464,7 @@ export const $MonetaryDto = {
7806
8464
  example: 'USD'
7807
8465
  },
7808
8466
  baseCcyEquivalent: {
7809
- type: 'object',
8467
+ type: 'string',
7810
8468
  description: 'Converted to user base currency (Decimal string)',
7811
8469
  example: '21600',
7812
8470
  nullable: true
@@ -7882,13 +8540,13 @@ export const $HoldingPnlRowDto = {
7882
8540
  example: 'Assets:US:Broker:AAPL'
7883
8541
  },
7884
8542
  accountCcy: {
7885
- type: 'object',
8543
+ type: 'string',
7886
8544
  description: 'Account settlement currency (ISO 4217), from cost currency',
7887
8545
  nullable: true,
7888
8546
  example: 'USD'
7889
8547
  },
7890
8548
  brokerType: {
7891
- type: 'object',
8549
+ type: 'string',
7892
8550
  description: 'Broker type derived from Platform.type',
7893
8551
  nullable: true,
7894
8552
  example: 'broker'
@@ -7909,7 +8567,7 @@ export const $HoldingPnlRowDto = {
7909
8567
  example: 'EQUITY'
7910
8568
  },
7911
8569
  assetSubClass: {
7912
- type: 'object',
8570
+ type: 'string',
7913
8571
  nullable: true,
7914
8572
  example: 'STOCK'
7915
8573
  },
@@ -7956,14 +8614,14 @@ export const $HoldingPnlRowDto = {
7956
8614
  ]
7957
8615
  },
7958
8616
  unrealizedPnlBase: {
7959
- type: 'object',
8617
+ type: 'string',
7960
8618
  description:
7961
8619
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7962
8620
  nullable: true,
7963
8621
  example: '6000'
7964
8622
  },
7965
8623
  unrealizedPnlPct: {
7966
- type: 'object',
8624
+ type: 'string',
7967
8625
  description: 'Unrealized P&L % (Decimal string)',
7968
8626
  nullable: true,
7969
8627
  example: '25'
@@ -7987,7 +8645,7 @@ export const $HoldingPnlRowDto = {
7987
8645
  ]
7988
8646
  },
7989
8647
  pctOfInvestedAssets: {
7990
- type: 'object',
8648
+ type: 'string',
7991
8649
  description:
7992
8650
  'Share of invested assets % (Decimal string); only for invested chartTokens',
7993
8651
  nullable: true,
@@ -8032,15 +8690,15 @@ export const $HoldingPnlWarningDto = {
8032
8690
  ]
8033
8691
  },
8034
8692
  symbol: {
8035
- type: 'object',
8693
+ type: 'string',
8036
8694
  nullable: true
8037
8695
  },
8038
8696
  accountId: {
8039
- type: 'object',
8697
+ type: 'string',
8040
8698
  nullable: true
8041
8699
  },
8042
8700
  currency: {
8043
- type: 'object',
8701
+ type: 'string',
8044
8702
  nullable: true
8045
8703
  }
8046
8704
  },
@@ -8103,3 +8761,356 @@ export const $AnonymousLoginResponseDto = {
8103
8761
  },
8104
8762
  required: ['authToken']
8105
8763
  } as const;
8764
+
8765
+ export const $ParserContributionMetaDto = {
8766
+ type: 'object',
8767
+ properties: {
8768
+ institution: {
8769
+ type: 'string',
8770
+ description: 'Institution slug (lowercase kebab-case)',
8771
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8772
+ example: 'icbc'
8773
+ },
8774
+ region: {
8775
+ type: 'string',
8776
+ enum: [
8777
+ 'cn',
8778
+ 'us',
8779
+ 'de',
8780
+ 'fr',
8781
+ 'gb',
8782
+ 'hk',
8783
+ 'jp',
8784
+ 'sg',
8785
+ 'au',
8786
+ 'ca',
8787
+ 'other'
8788
+ ]
8789
+ },
8790
+ accountType: {
8791
+ type: 'string',
8792
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
8793
+ },
8794
+ format: {
8795
+ type: 'string',
8796
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
8797
+ },
8798
+ institutionDisplayName: {
8799
+ type: 'string',
8800
+ example: '中国工商银行'
8801
+ },
8802
+ encoding: {
8803
+ type: 'string',
8804
+ example: 'utf-8'
8805
+ },
8806
+ delimiter: {
8807
+ type: 'string',
8808
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
8809
+ },
8810
+ headerRows: {
8811
+ type: 'number',
8812
+ default: 1,
8813
+ description: 'Header row count; the client omits the field when it is 1'
8814
+ },
8815
+ notes: {
8816
+ type: 'string',
8817
+ maxLength: 2000
8818
+ }
8819
+ },
8820
+ required: ['institution', 'region', 'accountType', 'format']
8821
+ } as const;
8822
+
8823
+ export const $ParserContributionSamplesDto = {
8824
+ type: 'object',
8825
+ properties: {
8826
+ rows: {
8827
+ description:
8828
+ 'Client-sanitized sample rows (key = column name, value = cell)',
8829
+ type: 'array',
8830
+ items: {
8831
+ type: 'object'
8832
+ }
8833
+ },
8834
+ rawHeaders: {
8835
+ type: 'array',
8836
+ items: {
8837
+ type: 'string'
8838
+ }
8839
+ }
8840
+ },
8841
+ required: ['rows']
8842
+ } as const;
8843
+
8844
+ export const $FieldHintDto = {
8845
+ type: 'object',
8846
+ properties: {
8847
+ columnName: {
8848
+ type: 'string',
8849
+ example: '交易日期'
8850
+ },
8851
+ format: {
8852
+ type: 'string',
8853
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8854
+ example: 'yyyy-MM-dd'
8855
+ },
8856
+ signConvention: {
8857
+ type: 'string',
8858
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
8859
+ },
8860
+ creditColumn: {
8861
+ type: 'string'
8862
+ },
8863
+ debitColumn: {
8864
+ type: 'string'
8865
+ }
8866
+ },
8867
+ required: ['columnName']
8868
+ } as const;
8869
+
8870
+ export const $ParserContributionFieldHintsDto = {
8871
+ type: 'object',
8872
+ properties: {
8873
+ date: {
8874
+ $ref: '#/components/schemas/FieldHintDto'
8875
+ },
8876
+ amount: {
8877
+ $ref: '#/components/schemas/FieldHintDto'
8878
+ },
8879
+ description: {
8880
+ $ref: '#/components/schemas/FieldHintDto'
8881
+ },
8882
+ balance: {
8883
+ $ref: '#/components/schemas/FieldHintDto'
8884
+ },
8885
+ payee: {
8886
+ $ref: '#/components/schemas/FieldHintDto'
8887
+ },
8888
+ reference: {
8889
+ $ref: '#/components/schemas/FieldHintDto'
8890
+ },
8891
+ category: {
8892
+ $ref: '#/components/schemas/FieldHintDto'
8893
+ }
8894
+ },
8895
+ required: ['date', 'amount']
8896
+ } as const;
8897
+
8898
+ export const $ExpectedTransactionDto = {
8899
+ type: 'object',
8900
+ properties: {
8901
+ date: {
8902
+ type: 'string',
8903
+ example: '2026-08-01'
8904
+ },
8905
+ amount: {
8906
+ type: 'number',
8907
+ example: -45.5
8908
+ },
8909
+ description: {
8910
+ type: 'string',
8911
+ example: '星巴克-***店'
8912
+ },
8913
+ payee: {
8914
+ type: 'string'
8915
+ },
8916
+ category: {
8917
+ type: 'string'
8918
+ }
8919
+ },
8920
+ required: ['date', 'amount', 'description']
8921
+ } as const;
8922
+
8923
+ export const $ParserContributionExamplesDto = {
8924
+ type: 'object',
8925
+ properties: {
8926
+ expectedTransactions: {
8927
+ type: 'array',
8928
+ items: {
8929
+ $ref: '#/components/schemas/ExpectedTransactionDto'
8930
+ }
8931
+ }
8932
+ },
8933
+ required: ['expectedTransactions']
8934
+ } as const;
8935
+
8936
+ export const $ParserContributionRequestDto = {
8937
+ type: 'object',
8938
+ properties: {
8939
+ meta: {
8940
+ $ref: '#/components/schemas/ParserContributionMetaDto'
8941
+ },
8942
+ samples: {
8943
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
8944
+ },
8945
+ fieldHints: {
8946
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
8947
+ },
8948
+ examples: {
8949
+ description: 'Omitted entirely by the client when empty',
8950
+ allOf: [
8951
+ {
8952
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8953
+ }
8954
+ ]
8955
+ }
8956
+ },
8957
+ required: ['meta', 'samples', 'fieldHints']
8958
+ } as const;
8959
+
8960
+ export const $ParserContributionRelayResponseDto = {
8961
+ type: 'object',
8962
+ properties: {
8963
+ issueUrl: {
8964
+ type: 'string',
8965
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
8966
+ },
8967
+ issueNumber: {
8968
+ type: 'number',
8969
+ example: 42
8970
+ }
8971
+ },
8972
+ required: ['issueUrl', 'issueNumber']
8973
+ } as const;
8974
+
8975
+ export const $SymbolSearchResultDto = {
8976
+ type: 'object',
8977
+ properties: {
8978
+ symbol: {
8979
+ type: 'string',
8980
+ example: 'AAPL'
8981
+ },
8982
+ name: {
8983
+ type: 'string',
8984
+ example: 'Apple Inc.',
8985
+ nullable: true
8986
+ },
8987
+ exchange: {
8988
+ type: 'string',
8989
+ example: 'US',
8990
+ nullable: true
8991
+ },
8992
+ assetType: {
8993
+ type: 'string',
8994
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8995
+ example: 'stock',
8996
+ nullable: true
8997
+ },
8998
+ assetClass: {
8999
+ type: 'string',
9000
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
9001
+ example: 'EQUITY',
9002
+ nullable: true
9003
+ },
9004
+ assetSubClass: {
9005
+ type: 'string',
9006
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
9007
+ example: 'STOCK',
9008
+ nullable: true
9009
+ },
9010
+ currency: {
9011
+ type: 'string',
9012
+ description: 'Trading currency (extra_data or inferred from exchange)',
9013
+ example: 'USD',
9014
+ nullable: true
9015
+ }
9016
+ },
9017
+ required: ['symbol']
9018
+ } as const;
9019
+
9020
+ export const $SymbolQuoteDto = {
9021
+ type: 'object',
9022
+ properties: {
9023
+ symbol: {
9024
+ type: 'string',
9025
+ example: 'AAPL'
9026
+ },
9027
+ name: {
9028
+ type: 'string',
9029
+ example: 'Apple Inc.',
9030
+ nullable: true
9031
+ },
9032
+ exchange: {
9033
+ type: 'string',
9034
+ example: 'US',
9035
+ nullable: true
9036
+ },
9037
+ assetType: {
9038
+ type: 'string',
9039
+ description: 'OpenBB asset_type',
9040
+ example: 'stock',
9041
+ nullable: true
9042
+ },
9043
+ assetClass: {
9044
+ type: 'string',
9045
+ description: 'IGN asset class',
9046
+ example: 'EQUITY',
9047
+ nullable: true
9048
+ },
9049
+ assetSubClass: {
9050
+ type: 'string',
9051
+ description: 'IGN asset sub-class',
9052
+ example: 'STOCK',
9053
+ nullable: true
9054
+ },
9055
+ currency: {
9056
+ type: 'string',
9057
+ description: 'Trading currency (extra_data or inferred from exchange)',
9058
+ example: 'USD',
9059
+ nullable: true
9060
+ },
9061
+ price: {
9062
+ type: 'string',
9063
+ description: 'Latest price (Decimal string)',
9064
+ example: '189.84',
9065
+ nullable: true
9066
+ },
9067
+ priceDate: {
9068
+ type: 'string',
9069
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
9070
+ example: '2026-08-05',
9071
+ nullable: true
9072
+ },
9073
+ changePercent: {
9074
+ type: 'number',
9075
+ description:
9076
+ 'Change vs previous close, in percentage points (1.7 == 1.7%). openbb stores change_percent as a normalized decimal; this exposes percentage points for frontend convenience.',
9077
+ example: 1.7,
9078
+ nullable: true
9079
+ },
9080
+ prevClose: {
9081
+ type: 'string',
9082
+ description: 'Previous close (Decimal string)',
9083
+ nullable: true
9084
+ },
9085
+ open: {
9086
+ type: 'string',
9087
+ description: 'Day open (Decimal string)',
9088
+ nullable: true
9089
+ },
9090
+ high: {
9091
+ type: 'string',
9092
+ description: 'Day high (Decimal string)',
9093
+ nullable: true
9094
+ },
9095
+ low: {
9096
+ type: 'string',
9097
+ description: 'Day low (Decimal string)',
9098
+ nullable: true
9099
+ },
9100
+ volume: {
9101
+ type: 'string',
9102
+ description: 'Day volume (Decimal string)',
9103
+ nullable: true
9104
+ },
9105
+ yearHigh: {
9106
+ type: 'string',
9107
+ description: '52-week high (Decimal string)',
9108
+ nullable: true
9109
+ },
9110
+ yearLow: {
9111
+ type: 'string',
9112
+ description: '52-week low (Decimal string)',
9113
+ nullable: true
9114
+ }
9115
+ }
9116
+ } as const;