@firela/api-types 0.0.0-canary.7b647d33 → 0.0.0-canary.824fea47

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.
@@ -6,18 +6,12 @@ export const $CreateAccountDto = {
6
6
  path: {
7
7
  type: 'string',
8
8
  description: 'Account path (hierarchical, colon-separated)',
9
- example: 'Assets:CN:Bank:ICBC:Checking'
10
- },
11
- displayName: {
12
- type: 'string',
13
- description:
14
- 'Display name to distinguish accounts at the same path (default: "")',
15
- example: '工资卡'
9
+ example: 'Assets:CN:ICBC:Checking'
16
10
  },
17
11
  openDate: {
18
12
  format: 'date-time',
19
13
  type: 'string',
20
- description: 'Account open date',
14
+ description: 'Account open date (server defaults to today)',
21
15
  example: '2024-01-01'
22
16
  },
23
17
  currencies: {
@@ -45,26 +39,22 @@ export const $CreateAccountDto = {
45
39
  templatePath: {
46
40
  type: 'string',
47
41
  description: 'Reference to account-standards template path',
48
- example: 'Assets:CN:Bank:ICBC:Checking'
42
+ example: 'Assets:CN:Checking'
49
43
  },
50
44
  isCustom: {
51
45
  type: 'boolean',
52
46
  description: 'Whether this is a custom (user-created) account',
53
47
  default: false
54
48
  },
55
- i18nKey: {
56
- type: 'string',
57
- description: 'i18n key for display name (overrides template)',
58
- example: 'account.custom.mybank'
59
- },
60
49
  icon: {
61
50
  type: 'string',
62
51
  description: 'Icon identifier (overrides template)',
63
52
  example: 'bank-custom'
64
53
  },
65
- openMeta: {
54
+ openDirectiveMeta: {
66
55
  type: 'object',
67
- description: 'Additional metadata',
56
+ description:
57
+ 'Open directive metadata (NOT an opening-balance amount — use the opening-balance endpoint)',
68
58
  example: {
69
59
  branch: 'Downtown',
70
60
  accountNumber: '1234'
@@ -76,7 +66,7 @@ export const $CreateAccountDto = {
76
66
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
77
67
  }
78
68
  },
79
- required: ['path', 'openDate']
69
+ required: ['path']
80
70
  } as const;
81
71
 
82
72
  export const $AccountResponseDto = {
@@ -90,13 +80,7 @@ export const $AccountResponseDto = {
90
80
  path: {
91
81
  type: 'string',
92
82
  description: 'Account path (hierarchical, colon-separated)',
93
- example: 'Assets:CN:Bank:ICBC:Checking'
94
- },
95
- displayName: {
96
- type: 'string',
97
- description:
98
- 'Display name distinguishing multiple accounts at the same path',
99
- example: '工资卡'
83
+ example: 'Assets:CN:ICBC:Checking'
100
84
  },
101
85
  type: {
102
86
  type: 'string',
@@ -104,6 +88,47 @@ export const $AccountResponseDto = {
104
88
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
105
89
  example: 'Assets'
106
90
  },
91
+ assetSubClass: {
92
+ type: 'string',
93
+ description:
94
+ 'Account-level asset sub-class (product type, e.g. STOCK/DEPOSIT/CREDIT_CARD/PERSONAL_LOAN). Computed from the account path via the asset-classifier (ADR-0077). Null for non-asset accounts (Income/Expenses/Equity) or unmatched paths.',
95
+ enum: [
96
+ 'DEPOSIT',
97
+ 'CASH',
98
+ 'MONEY_MARKET_FUND',
99
+ 'STOCK',
100
+ 'ETF',
101
+ 'MUTUAL_FUND',
102
+ 'EQUITY_COMPENSATION',
103
+ 'GOVERNMENT_BOND',
104
+ 'CORPORATE_BOND',
105
+ 'BOND_FUND',
106
+ 'PRIMARY_RESIDENCE',
107
+ 'INVESTMENT_PROPERTY',
108
+ 'REIT',
109
+ 'GOLD',
110
+ 'SILVER',
111
+ 'PRECIOUS_METAL',
112
+ 'PRECIOUS_METAL_FUND',
113
+ 'COMMODITY',
114
+ 'COMMODITY_FUND',
115
+ 'CRYPTOCURRENCY',
116
+ 'RETIREMENT_ACCOUNT',
117
+ 'HEALTH_ACCOUNT',
118
+ 'EDUCATION_ACCOUNT',
119
+ 'INSURANCE',
120
+ 'PRIVATE_EQUITY',
121
+ 'HEDGE_FUND',
122
+ 'COLLECTIBLES',
123
+ 'MORTGAGE',
124
+ 'STUDENT_LOAN',
125
+ 'CREDIT_CARD',
126
+ 'PERSONAL_LOAN',
127
+ 'OTHER'
128
+ ],
129
+ nullable: true,
130
+ example: 'STOCK'
131
+ },
107
132
  status: {
108
133
  type: 'string',
109
134
  description: 'Account status',
@@ -145,26 +170,26 @@ export const $AccountResponseDto = {
145
170
  templatePath: {
146
171
  type: 'string',
147
172
  description: 'Template path reference',
148
- example: 'Assets:CN:Bank:ICBC:Checking'
173
+ example: 'Assets:CN:Checking'
149
174
  },
150
175
  isCustom: {
151
176
  type: 'boolean',
152
177
  description: 'Whether this is a custom (user-created) account',
153
178
  example: false
154
179
  },
155
- i18nKey: {
180
+ displayName: {
156
181
  type: 'string',
157
- description: 'i18n key for display name',
158
- example: 'account.assets.cn.bank.icbc.checking'
182
+ description: 'Localized display name (ADR-0114, read-time projection)',
183
+ example: 'Checking'
159
184
  },
160
185
  icon: {
161
186
  type: 'string',
162
187
  description: 'Icon identifier',
163
188
  example: 'bank-icbc'
164
189
  },
165
- openMeta: {
190
+ openDirectiveMeta: {
166
191
  type: 'object',
167
- description: 'Account metadata',
192
+ description: 'Open directive metadata (ADR-0115 Decision 9)',
168
193
  example: {
169
194
  branch: 'Downtown'
170
195
  }
@@ -192,7 +217,6 @@ export const $AccountResponseDto = {
192
217
  required: [
193
218
  'id',
194
219
  'path',
195
- 'displayName',
196
220
  'type',
197
221
  'status',
198
222
  'openDate',
@@ -225,11 +249,6 @@ export const $AccountListResponseDto = {
225
249
  export const $UpdateAccountDto = {
226
250
  type: 'object',
227
251
  properties: {
228
- displayName: {
229
- type: 'string',
230
- description: 'Display name to distinguish accounts at the same path',
231
- example: '招行工资卡'
232
- },
233
252
  currencies: {
234
253
  description: 'Allowed currencies (null = no restriction)',
235
254
  example: ['CNY', 'USD'],
@@ -251,19 +270,15 @@ export const $UpdateAccountDto = {
251
270
  'NONE'
252
271
  ]
253
272
  },
254
- i18nKey: {
255
- type: 'string',
256
- description: 'i18n key for display name',
257
- example: 'account.custom.mybank'
258
- },
259
273
  icon: {
260
274
  type: 'string',
261
275
  description: 'Icon identifier',
262
276
  example: 'bank-custom'
263
277
  },
264
- openMeta: {
278
+ openDirectiveMeta: {
265
279
  type: 'object',
266
- description: 'Additional metadata (merged with existing)',
280
+ description:
281
+ 'Open directive metadata (merged with existing; NOT an opening-balance amount)',
267
282
  example: {
268
283
  branch: 'Uptown'
269
284
  }
@@ -310,13 +325,47 @@ export const $ReopenAccountDto = {
310
325
  }
311
326
  } as const;
312
327
 
328
+ export const $CreateOpeningBalanceDto = {
329
+ type: 'object',
330
+ properties: {
331
+ amount: {
332
+ type: 'number',
333
+ description: 'Opening balance amount (non-negative)',
334
+ example: 1000
335
+ },
336
+ currency: {
337
+ type: 'string',
338
+ description: 'Currency code',
339
+ example: 'CNY'
340
+ },
341
+ date: {
342
+ format: 'date-time',
343
+ type: 'string',
344
+ description: 'Opening-balance date (defaults to now)',
345
+ example: '2024-01-01'
346
+ }
347
+ },
348
+ required: ['amount', 'currency']
349
+ } as const;
350
+
351
+ export const $OpeningBalanceResultDto = {
352
+ type: 'object',
353
+ properties: {
354
+ transactionId: {
355
+ type: 'string',
356
+ description: 'Created opening-balance transaction id.'
357
+ }
358
+ },
359
+ required: ['transactionId']
360
+ } as const;
361
+
313
362
  export const $AccountStandardResponseDto = {
314
363
  type: 'object',
315
364
  properties: {
316
365
  path: {
317
366
  type: 'string',
318
367
  description: 'Account path (hierarchical, colon-separated)',
319
- example: 'Assets:CN:Bank:ICBC:Checking'
368
+ example: 'Assets:CN:Checking'
320
369
  },
321
370
  type: {
322
371
  type: 'string',
@@ -324,11 +373,6 @@ export const $AccountStandardResponseDto = {
324
373
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
325
374
  example: 'Assets'
326
375
  },
327
- i18nKey: {
328
- type: 'string',
329
- description: 'i18n key for localized display name',
330
- example: 'account.assets.cn.bank.icbc.checking'
331
- },
332
376
  name: {
333
377
  type: 'string',
334
378
  description: 'Short localized display name',
@@ -353,7 +397,7 @@ export const $AccountStandardResponseDto = {
353
397
  example: 'bank-icbc'
354
398
  }
355
399
  },
356
- required: ['path', 'type', 'i18nKey', 'description', 'tags', 'icon']
400
+ required: ['path', 'type', 'description', 'tags', 'icon']
357
401
  } as const;
358
402
 
359
403
  export const $AccountStandardListResponseDto = {
@@ -383,18 +427,13 @@ export const $AccountStandardListResponseDto = {
383
427
  export const $TemplateMetadataDto = {
384
428
  type: 'object',
385
429
  properties: {
386
- extendable: {
387
- type: 'boolean',
388
- description: 'Whether this path can be extended',
389
- example: true
390
- },
391
430
  rootType: {
392
431
  type: 'string',
393
432
  description: 'Root account type',
394
433
  example: 'Assets'
395
434
  }
396
435
  },
397
- required: ['extendable', 'rootType']
436
+ required: ['rootType']
398
437
  } as const;
399
438
 
400
439
  export const $TemplateMetadataResponseDto = {
@@ -533,7 +572,7 @@ export const $CreatePostingDto = {
533
572
  type: 'string',
534
573
  description:
535
574
  'Account name in Beancount format (must start with uppercase, colon-separated)',
536
- example: 'Assets:Bank:Checking'
575
+ example: 'Assets:Checking'
537
576
  },
538
577
  units: {
539
578
  type: 'string',
@@ -692,7 +731,7 @@ export const $PostingResponseDto = {
692
731
  account: {
693
732
  type: 'string',
694
733
  description: 'Account name',
695
- example: 'Assets:Bank:Checking'
734
+ example: 'Assets:Checking'
696
735
  },
697
736
  units: {
698
737
  type: 'string',
@@ -1058,7 +1097,7 @@ export const $PostingDetailDto = {
1058
1097
  account: {
1059
1098
  type: 'string',
1060
1099
  description: 'Fully-qualified Beancount account path',
1061
- example: 'Assets:Bank:Checking'
1100
+ example: 'Assets:Checking'
1062
1101
  },
1063
1102
  units: {
1064
1103
  type: 'string',
@@ -1247,6 +1286,77 @@ export const $TransactionDetailDto = {
1247
1286
  ]
1248
1287
  } as const;
1249
1288
 
1289
+ export const $BalanceByCurrencyDto = {
1290
+ type: 'object',
1291
+ properties: {
1292
+ currency: {
1293
+ type: 'string',
1294
+ description: 'ISO 4217 currency code',
1295
+ example: 'CNY'
1296
+ },
1297
+ balance: {
1298
+ type: 'string',
1299
+ description: 'Balance amount',
1300
+ example: '50000.00'
1301
+ }
1302
+ },
1303
+ required: ['currency', 'balance']
1304
+ } as const;
1305
+
1306
+ export const $ExchangeRateWarningDto = {
1307
+ type: 'object',
1308
+ properties: {
1309
+ type: {
1310
+ type: 'string',
1311
+ description: 'Warning type',
1312
+ example: 'MISSING_EXCHANGE_RATE'
1313
+ },
1314
+ currency: {
1315
+ type: 'string',
1316
+ description: 'Currency without exchange rate',
1317
+ example: 'EUR'
1318
+ },
1319
+ totalAmount: {
1320
+ type: 'string',
1321
+ description: 'Total amount affected',
1322
+ example: '1000.00'
1323
+ }
1324
+ },
1325
+ required: ['type', 'currency', 'totalAmount']
1326
+ } as const;
1327
+
1328
+ export const $TransactionListSummaryDto = {
1329
+ type: 'object',
1330
+ properties: {
1331
+ totalAmount: {
1332
+ type: 'string',
1333
+ 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.',
1335
+ example: '-6000.00'
1336
+ },
1337
+ currency: {
1338
+ type: 'string',
1339
+ description: 'Base currency (ISO 4217)',
1340
+ example: 'CNY'
1341
+ },
1342
+ balanceByCurrency: {
1343
+ description: 'Raw (unconverted) balance per currency',
1344
+ type: 'array',
1345
+ items: {
1346
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
1347
+ }
1348
+ },
1349
+ warnings: {
1350
+ description: 'Currencies missing an FX rate (omitted when empty)',
1351
+ type: 'array',
1352
+ items: {
1353
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
1354
+ }
1355
+ }
1356
+ },
1357
+ required: ['totalAmount', 'currency', 'balanceByCurrency']
1358
+ } as const;
1359
+
1250
1360
  export const $TransactionListResponseDto = {
1251
1361
  type: 'object',
1252
1362
  properties: {
@@ -1271,6 +1381,15 @@ export const $TransactionListResponseDto = {
1271
1381
  type: 'number',
1272
1382
  description: 'Number of items skipped',
1273
1383
  example: 0
1384
+ },
1385
+ summary: {
1386
+ description:
1387
+ 'Amount summary for the full filtered set (#514). Present only when the request has a single account OR category viewpoint; omitted for search-only / plain-list / dual-perspective requests.',
1388
+ allOf: [
1389
+ {
1390
+ $ref: '#/components/schemas/TransactionListSummaryDto'
1391
+ }
1392
+ ]
1274
1393
  }
1275
1394
  },
1276
1395
  required: ['data', 'total', 'limit', 'offset']
@@ -1370,7 +1489,7 @@ export const $BalanceResponseDto = {
1370
1489
  account: {
1371
1490
  type: 'string',
1372
1491
  description: 'Account name',
1373
- example: 'Assets:Bank:Checking'
1492
+ example: 'Assets:Checking'
1374
1493
  },
1375
1494
  balance: {
1376
1495
  type: 'string',
@@ -1397,7 +1516,7 @@ export const $MultiCurrencyBalanceResponseDto = {
1397
1516
  account: {
1398
1517
  type: 'string',
1399
1518
  description: 'Account name',
1400
- example: 'Assets:Bank:Checking'
1519
+ example: 'Assets:Checking'
1401
1520
  },
1402
1521
  balances: {
1403
1522
  type: 'object',
@@ -1453,7 +1572,7 @@ export const $TransactionSummaryDto = {
1453
1572
  accountName: {
1454
1573
  type: 'string',
1455
1574
  description: 'Source account name (first posting)',
1456
- example: 'Assets:Bank:Checking'
1575
+ example: 'Assets:Checking'
1457
1576
  },
1458
1577
  sourceType: {
1459
1578
  type: 'string',
@@ -2745,117 +2864,274 @@ export const $UpdateCommodityDto = {
2745
2864
  }
2746
2865
  } as const;
2747
2866
 
2748
- export const $CreateRecurringRuleDto = {
2867
+ export const $CreateBeanPriceDto = {
2749
2868
  type: 'object',
2750
2869
  properties: {
2751
- name: {
2752
- type: 'string',
2753
- description: 'Rule name (unique per user)',
2754
- maxLength: 100
2755
- },
2756
- icon: {
2757
- type: 'string',
2758
- description: 'Icon emoji',
2759
- maxLength: 10
2760
- },
2761
- frequency: {
2762
- type: 'string',
2763
- description: 'Recurring frequency',
2764
- enum: [
2765
- 'WEEKLY',
2766
- 'BIWEEKLY',
2767
- 'MONTHLY',
2768
- 'BIMONTHLY',
2769
- 'QUARTERLY',
2770
- 'YEARLY',
2771
- 'CUSTOM'
2772
- ]
2773
- },
2774
- expectedAmount: {
2775
- type: 'number',
2776
- description: 'Expected amount (positive number)',
2777
- minimum: 0
2778
- },
2779
- expectedDay: {
2780
- type: 'number',
2781
- description: 'Expected day of month (1-31)',
2782
- minimum: 1,
2783
- maximum: 31
2784
- },
2785
- customIntervalDays: {
2786
- type: 'number',
2787
- description: 'Custom interval in days (required for CUSTOM frequency)',
2788
- minimum: 1
2789
- },
2790
2870
  currency: {
2791
2871
  type: 'string',
2792
- description: 'Currency code',
2793
- default: 'CNY',
2794
- maxLength: 10
2872
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2873
+ example: 'USD'
2795
2874
  },
2796
- matchPayeePattern: {
2875
+ quoteCurrency: {
2797
2876
  type: 'string',
2798
- description: 'Payee matching pattern (supports wildcards)',
2799
- maxLength: 200
2877
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
2878
+ example: 'CNY'
2800
2879
  },
2801
- matchAmountTolerance: {
2880
+ amount: {
2802
2881
  type: 'number',
2803
- description: 'Amount tolerance percentage (0-1)',
2804
- default: 0.075,
2805
- minimum: 0,
2806
- maximum: 1
2807
- },
2808
- defaultExpenseAccount: {
2809
- type: 'string',
2810
- description: 'Default expense account for auto-create',
2811
- maxLength: 200
2812
- },
2813
- defaultPaymentAccount: {
2814
- type: 'string',
2815
- description: 'Default payment account for auto-create',
2816
- maxLength: 200
2817
- },
2818
- defaultPayee: {
2819
- type: 'string',
2820
- description: 'Default payee for auto-create',
2821
- maxLength: 200
2822
- },
2823
- autoCreate: {
2824
- type: 'boolean',
2825
- description: 'Auto-create transaction when expected date arrives',
2826
- default: false
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
2827
2886
  },
2828
- startDate: {
2887
+ date: {
2829
2888
  type: 'string',
2830
- description: 'Rule start date (ISO format)'
2889
+ description: 'Price date (ISO 8601 format)',
2890
+ example: '2024-11-05'
2831
2891
  },
2832
- endDate: {
2833
- type: 'string',
2834
- description: 'Rule end date (ISO format)'
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
+ }
2835
2901
  }
2836
2902
  },
2837
- required: [
2838
- 'name',
2839
- 'frequency',
2840
- 'expectedAmount',
2841
- 'currency',
2842
- 'matchAmountTolerance',
2843
- 'autoCreate'
2844
- ]
2903
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
2845
2904
  } as const;
2846
2905
 
2847
- export const $RecurringRuleResponseDto = {
2906
+ export const $PriceResponseDto = {
2848
2907
  type: 'object',
2849
2908
  properties: {
2850
2909
  id: {
2851
2910
  type: 'string',
2852
- description: 'Rule ID'
2911
+ description: 'Unique identifier',
2912
+ example: 'uuid-123-456'
2853
2913
  },
2854
2914
  userId: {
2855
2915
  type: 'string',
2856
- description: 'User ID'
2916
+ description: 'User ID (owner of the price)',
2917
+ example: 'user-123'
2857
2918
  },
2858
- name: {
2919
+ currency: {
2920
+ type: 'string',
2921
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
2922
+ example: 'BTC'
2923
+ },
2924
+ quoteCurrency: {
2925
+ 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
2934
+ },
2935
+ date: {
2936
+ 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'
2941
+ },
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
2950
+ }
2951
+ },
2952
+ createdAt: {
2953
+ format: 'date-time',
2954
+ type: 'string',
2955
+ description: 'Creation timestamp',
2956
+ example: '2024-11-03T10:00:00Z'
2957
+ },
2958
+ updatedAt: {
2959
+ format: 'date-time',
2960
+ type: 'string',
2961
+ description: 'Last update timestamp',
2962
+ example: '2024-11-03T10:00:00Z'
2963
+ }
2964
+ },
2965
+ required: [
2966
+ 'id',
2967
+ 'userId',
2968
+ 'currency',
2969
+ 'quoteCurrency',
2970
+ 'amount',
2971
+ 'date',
2972
+ 'meta',
2973
+ 'createdAt',
2974
+ 'updatedAt'
2975
+ ]
2976
+ } as const;
2977
+
2978
+ export const $PriceListResponseDto = {
2979
+ type: 'object',
2980
+ properties: {
2981
+ items: {
2982
+ description: 'List of prices',
2983
+ type: 'array',
2984
+ items: {
2985
+ $ref: '#/components/schemas/PriceResponseDto'
2986
+ }
2987
+ },
2988
+ total: {
2989
+ type: 'number',
2990
+ description: 'Total number of prices',
2991
+ example: 42
2992
+ }
2993
+ },
2994
+ required: ['items', 'total']
2995
+ } as const;
2996
+
2997
+ export const $UpdateBeanPriceDto = {
2998
+ type: 'object',
2999
+ properties: {
3000
+ currency: {
3001
+ type: 'string',
3002
+ description: 'Currency being priced'
3003
+ },
3004
+ quoteCurrency: {
3005
+ type: 'string',
3006
+ description: 'Quote currency (pricing currency)'
3007
+ },
3008
+ amount: {
3009
+ type: 'number',
3010
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3011
+ minimum: 0
3012
+ },
3013
+ date: {
3014
+ type: 'string',
3015
+ description: 'Price date (ISO 8601 format)'
3016
+ },
3017
+ metadata: {
3018
+ type: 'object',
3019
+ description: 'Metadata'
3020
+ }
3021
+ }
3022
+ } as const;
3023
+
3024
+ export const $CreateRecurringRuleDto = {
3025
+ type: 'object',
3026
+ properties: {
3027
+ name: {
3028
+ type: 'string',
3029
+ description: 'Rule name (unique per user)',
3030
+ maxLength: 100
3031
+ },
3032
+ icon: {
3033
+ type: 'string',
3034
+ description: 'Icon emoji',
3035
+ maxLength: 10
3036
+ },
3037
+ frequency: {
3038
+ 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
3065
+ },
3066
+ currency: {
3067
+ type: 'string',
3068
+ description: 'Currency code',
3069
+ default: 'CNY',
3070
+ maxLength: 10
3071
+ },
3072
+ matchPayeePattern: {
3073
+ 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
3083
+ },
3084
+ defaultExpenseAccount: {
3085
+ type: 'string',
3086
+ description: 'Default expense account for auto-create',
3087
+ maxLength: 200
3088
+ },
3089
+ defaultPaymentAccount: {
3090
+ type: 'string',
3091
+ description: 'Default payment account for auto-create',
3092
+ maxLength: 200
3093
+ },
3094
+ defaultPayee: {
3095
+ type: 'string',
3096
+ description: 'Default payee for auto-create',
3097
+ maxLength: 200
3098
+ },
3099
+ autoCreate: {
3100
+ type: 'boolean',
3101
+ description: 'Auto-create transaction when expected date arrives',
3102
+ default: false
3103
+ },
3104
+ startDate: {
3105
+ type: 'string',
3106
+ description: 'Rule start date (ISO format)'
3107
+ },
3108
+ endDate: {
3109
+ type: 'string',
3110
+ description: 'Rule end date (ISO format)'
3111
+ }
3112
+ },
3113
+ required: [
3114
+ 'name',
3115
+ 'frequency',
3116
+ 'expectedAmount',
3117
+ 'currency',
3118
+ 'matchAmountTolerance',
3119
+ 'autoCreate'
3120
+ ]
3121
+ } as const;
3122
+
3123
+ export const $RecurringRuleResponseDto = {
3124
+ type: 'object',
3125
+ properties: {
3126
+ id: {
3127
+ type: 'string',
3128
+ description: 'Rule ID'
3129
+ },
3130
+ userId: {
3131
+ type: 'string',
3132
+ description: 'User ID'
3133
+ },
3134
+ name: {
2859
3135
  type: 'string',
2860
3136
  description: 'Rule name'
2861
3137
  },
@@ -3516,30 +3792,477 @@ export const $ForecastResponseDto = {
3516
3792
  ]
3517
3793
  } as const;
3518
3794
 
3519
- export const $CreateTransactionRuleDto = {
3795
+ export const $CurrencyBalanceDto = {
3520
3796
  type: 'object',
3521
3797
  properties: {
3522
- name: {
3798
+ currency: {
3523
3799
  type: 'string',
3524
- minLength: 1,
3525
- maxLength: 100
3800
+ description: 'ISO 4217 currency code',
3801
+ example: 'CNY'
3526
3802
  },
3527
- description: {
3803
+ balance: {
3528
3804
  type: 'string',
3529
- maxLength: 500
3805
+ description: 'Balance amount',
3806
+ example: '500000.00'
3807
+ }
3808
+ },
3809
+ required: ['currency', 'balance']
3810
+ } as const;
3811
+
3812
+ export const $TimeSeriesPointDto = {
3813
+ type: 'object',
3814
+ properties: {
3815
+ date: {
3816
+ type: 'string',
3817
+ description: 'Date in YYYY-MM-DD format',
3818
+ example: '2024-06-15'
3530
3819
  },
3531
- narrationKeywords: {
3532
- items: {
3533
- type: 'array'
3534
- },
3535
- maxItems: 50,
3536
- type: 'array'
3820
+ value: {
3821
+ type: 'string',
3822
+ description: 'Value at this date (in base currency)',
3823
+ example: '500000.00'
3537
3824
  },
3538
- payeeKeywords: {
3539
- items: {
3540
- type: 'array'
3541
- },
3542
- maxItems: 50,
3825
+ change: {
3826
+ type: 'object',
3827
+ description: 'Change from previous point',
3828
+ example: '5000.00'
3829
+ },
3830
+ assets: {
3831
+ type: 'string',
3832
+ description: 'Total assets at this date (in base currency)',
3833
+ example: '494338.00'
3834
+ },
3835
+ liabilities: {
3836
+ type: 'string',
3837
+ description: 'Total liabilities at this date (in base currency)',
3838
+ example: '310098.00'
3839
+ },
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'
3858
+ },
3859
+ endValue: {
3860
+ type: 'string',
3861
+ description: 'Value at end of period',
3862
+ example: '500000.00'
3863
+ },
3864
+ totalChange: {
3865
+ type: 'string',
3866
+ description: 'Total change over period',
3867
+ example: '50000.00'
3868
+ },
3869
+ totalChangePercentage: {
3870
+ type: 'string',
3871
+ description: 'Total change percentage',
3872
+ example: '+11.11%'
3873
+ }
3874
+ },
3875
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
3876
+ } as const;
3877
+
3878
+ export const $MultiCurrencyPointDto = {
3879
+ type: 'object',
3880
+ properties: {
3881
+ date: {
3882
+ type: 'string',
3883
+ description: 'Date in YYYY-MM-DD format',
3884
+ example: '2024-06-15'
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
+ }
3906
+ },
3907
+ summary: {
3908
+ description: 'Period summary',
3909
+ allOf: [
3910
+ {
3911
+ $ref: '#/components/schemas/TrendSummaryDto'
3912
+ }
3913
+ ]
3914
+ },
3915
+ period: {
3916
+ type: 'string',
3917
+ description: 'Period requested',
3918
+ example: '6m'
3919
+ },
3920
+ granularity: {
3921
+ type: 'string',
3922
+ description: 'Data granularity',
3923
+ example: 'month'
3924
+ },
3925
+ currency: {
3926
+ type: 'string',
3927
+ description: 'Base currency for converted values',
3928
+ example: 'CNY'
3929
+ },
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
+ }
3937
+ },
3938
+ warnings: {
3939
+ description: 'Exchange rate warnings',
3940
+ type: 'array',
3941
+ items: {
3942
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3943
+ }
3944
+ }
3945
+ },
3946
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3947
+ } as const;
3948
+
3949
+ export const $CashFlowPointDto = {
3950
+ type: 'object',
3951
+ properties: {
3952
+ month: {
3953
+ type: 'string',
3954
+ description: 'Month key (YYYY-MM)',
3955
+ example: '2024-03'
3956
+ },
3957
+ income: {
3958
+ type: 'string',
3959
+ description: 'Income in base currency (absolute, converted)',
3960
+ example: '10000.00'
3961
+ },
3962
+ expense: {
3963
+ type: 'string',
3964
+ description: 'Expense in base currency (absolute, converted)',
3965
+ example: '5000.00'
3966
+ },
3967
+ netSavings: {
3968
+ type: 'string',
3969
+ description: 'netSavings = income − expense (savings positive)',
3970
+ example: '5000.00'
3971
+ }
3972
+ },
3973
+ required: ['month', 'income', 'expense', 'netSavings']
3974
+ } as const;
3975
+
3976
+ export const $CashFlowTrendSummaryDto = {
3977
+ type: 'object',
3978
+ properties: {
3979
+ totalIncome: {
3980
+ type: 'string',
3981
+ description: 'Total income across the period',
3982
+ example: '60000.00'
3983
+ },
3984
+ totalExpense: {
3985
+ type: 'string',
3986
+ description: 'Total expense across the period',
3987
+ example: '30000.00'
3988
+ },
3989
+ totalNetSavings: {
3990
+ type: 'string',
3991
+ description: 'income − expense across the period',
3992
+ example: '30000.00'
3993
+ },
3994
+ averageMonthlyNetSavings: {
3995
+ 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
+ }
4019
+ },
4020
+ summary: {
4021
+ description: 'Period totals',
4022
+ allOf: [
4023
+ {
4024
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
4025
+ }
4026
+ ]
4027
+ },
4028
+ period: {
4029
+ type: 'string',
4030
+ description: 'Period requested',
4031
+ example: '6m'
4032
+ },
4033
+ granularity: {
4034
+ type: 'string',
4035
+ description: 'Data granularity (v1 returns month buckets)',
4036
+ example: 'month'
4037
+ },
4038
+ currency: {
4039
+ 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
+ }
4049
+ }
4050
+ },
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: {}
4072
+ } as const;
4073
+
4074
+ export const $DeleteOwnUserDto = {
4075
+ type: 'object',
4076
+ properties: {
4077
+ accessToken: {
4078
+ type: 'string',
4079
+ description: 'Access token for user verification',
4080
+ example: 'abc123xyz'
4081
+ }
4082
+ },
4083
+ required: ['accessToken']
4084
+ } as const;
4085
+
4086
+ export const $SignupDto = {
4087
+ type: 'object',
4088
+ properties: {
4089
+ turnstileToken: {
4090
+ type: 'string',
4091
+ description:
4092
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4093
+ example: '0.abc123def456...'
4094
+ }
4095
+ }
4096
+ } as const;
4097
+
4098
+ export const $SignupResponseDto = {
4099
+ type: 'object',
4100
+ properties: {
4101
+ authToken: {
4102
+ type: 'string',
4103
+ description: 'JWT auth token',
4104
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
4105
+ },
4106
+ accessToken: {
4107
+ type: 'string',
4108
+ description: 'Auto-generated access token'
4109
+ },
4110
+ role: {
4111
+ type: 'string',
4112
+ description: 'Assigned user role',
4113
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
4114
+ }
4115
+ },
4116
+ required: ['authToken', 'accessToken', 'role']
4117
+ } as const;
4118
+
4119
+ export const $UpdateUserSettingDto = {
4120
+ type: 'object',
4121
+ properties: {
4122
+ secId: {
4123
+ type: 'number',
4124
+ description: 'Security ID'
4125
+ },
4126
+ annualInterestRate: {
4127
+ type: 'number',
4128
+ description: 'Annual interest rate',
4129
+ example: 0.05
4130
+ },
4131
+ currency: {
4132
+ type: 'string',
4133
+ description: 'Currency code',
4134
+ example: 'USD'
4135
+ },
4136
+ baseCurrency: {
4137
+ type: 'string',
4138
+ description: 'Base currency code',
4139
+ example: 'USD'
4140
+ },
4141
+ benchmark: {
4142
+ type: 'string',
4143
+ description: 'Benchmark symbol',
4144
+ example: 'SPY'
4145
+ },
4146
+ colorScheme: {
4147
+ type: 'string',
4148
+ description: 'Color scheme',
4149
+ enum: ['DARK', 'LIGHT']
4150
+ },
4151
+ dateRange: {
4152
+ type: 'string',
4153
+ description: 'Date range filter',
4154
+ example: '1y'
4155
+ },
4156
+ emergencyFund: {
4157
+ type: 'number',
4158
+ description: 'Emergency fund amount',
4159
+ example: 10000
4160
+ },
4161
+ 'filters.accounts': {
4162
+ description: 'Account filter IDs',
4163
+ type: 'array',
4164
+ items: {
4165
+ type: 'string'
4166
+ }
4167
+ },
4168
+ 'filters.assetClasses': {
4169
+ description: 'Asset class filters',
4170
+ type: 'array',
4171
+ items: {
4172
+ type: 'string'
4173
+ }
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',
4185
+ type: 'array',
4186
+ items: {
4187
+ type: 'string'
4188
+ }
4189
+ },
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: {
4209
+ type: 'number',
4210
+ description: 'Projected total amount',
4211
+ example: 1000000
4212
+ },
4213
+ retirementDate: {
4214
+ type: 'string',
4215
+ description: 'Retirement date in ISO 8601 format',
4216
+ example: '2050-01-01'
4217
+ },
4218
+ savingsRate: {
4219
+ type: 'number',
4220
+ description: 'Savings rate percentage',
4221
+ example: 0.2
4222
+ },
4223
+ viewMode: {
4224
+ 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: {
4235
+ type: 'string',
4236
+ description: 'Property value'
4237
+ }
4238
+ },
4239
+ required: ['value']
4240
+ } as const;
4241
+
4242
+ export const $CreateTransactionRuleDto = {
4243
+ type: 'object',
4244
+ properties: {
4245
+ name: {
4246
+ type: 'string',
4247
+ minLength: 1,
4248
+ maxLength: 100
4249
+ },
4250
+ description: {
4251
+ type: 'string',
4252
+ maxLength: 500
4253
+ },
4254
+ narrationKeywords: {
4255
+ items: {
4256
+ type: 'array'
4257
+ },
4258
+ maxItems: 50,
4259
+ type: 'array'
4260
+ },
4261
+ payeeKeywords: {
4262
+ items: {
4263
+ type: 'array'
4264
+ },
4265
+ maxItems: 50,
3543
4266
  type: 'array'
3544
4267
  },
3545
4268
  categoryKeywords: {
@@ -4133,321 +4856,425 @@ export const $TestRuleDto = {
4133
4856
  maxLength: 10
4134
4857
  }
4135
4858
  },
4136
- required: ['narration']
4859
+ required: ['narration']
4860
+ } as const;
4861
+
4862
+ export const $TestRuleResponseDto = {
4863
+ type: 'object',
4864
+ 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'
4872
+ },
4873
+ confidence: {
4874
+ type: 'number',
4875
+ description: 'Match confidence score (0-1)',
4876
+ example: 0.85
4877
+ },
4878
+ matchDetails: {
4879
+ type: 'object',
4880
+ description: 'Details of which fields matched',
4881
+ example: {
4882
+ narration: true,
4883
+ payee: false,
4884
+ categoryAccount: false
4885
+ }
4886
+ }
4887
+ },
4888
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
4889
+ } as const;
4890
+
4891
+ export const $CreateBeanEventDto = {
4892
+ type: 'object',
4893
+ properties: {
4894
+ date: {
4895
+ type: 'string',
4896
+ description: 'Life event date (ISO 8601)',
4897
+ example: '2024-03-15'
4898
+ },
4899
+ type: {
4900
+ type: 'string',
4901
+ description:
4902
+ 'Life event type (e.g., "employer", "location", "marital-status") — user-defined, no enum constraint at engine layer',
4903
+ example: 'employer'
4904
+ },
4905
+ description: {
4906
+ type: 'string',
4907
+ description:
4908
+ 'Life event description. Empty string is a VALID value (distinct from absence).',
4909
+ example: 'Acme Corp'
4910
+ },
4911
+ meta: {
4912
+ type: 'object',
4913
+ description:
4914
+ 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
4915
+ example: {
4916
+ note: 'Promotion'
4917
+ }
4918
+ }
4919
+ },
4920
+ required: ['date', 'type', 'description']
4137
4921
  } as const;
4138
4922
 
4139
- export const $TestRuleResponseDto = {
4923
+ export const $EventResponseDto = {
4140
4924
  type: 'object',
4141
4925
  properties: {
4142
- ruleId: {
4926
+ id: {
4143
4927
  type: 'string',
4144
- description: 'Rule ID that was tested'
4928
+ description: 'Unique identifier',
4929
+ example: 'uuid-123-456'
4145
4930
  },
4146
- matches: {
4147
- type: 'boolean',
4148
- description: 'Whether the rule matched the test data'
4931
+ userId: {
4932
+ type: 'string',
4933
+ description: 'User ID (owner of the life event)',
4934
+ example: 'user-123'
4149
4935
  },
4150
- confidence: {
4151
- type: 'number',
4152
- description: 'Match confidence score (0-1)',
4153
- example: 0.85
4936
+ date: {
4937
+ type: 'string',
4938
+ description: 'Life event date (ISO 8601 format)',
4939
+ example: '2024-03-15',
4940
+ format: 'date'
4154
4941
  },
4155
- matchDetails: {
4942
+ type: {
4943
+ type: 'string',
4944
+ description:
4945
+ 'Life event type (user-defined, e.g., "employer", "location")',
4946
+ example: 'employer'
4947
+ },
4948
+ description: {
4949
+ type: 'string',
4950
+ description:
4951
+ 'Life event description. May be an empty string (a valid value distinct from absence).',
4952
+ example: 'Acme Corp'
4953
+ },
4954
+ meta: {
4156
4955
  type: 'object',
4157
- description: 'Details of which fields matched',
4956
+ description: 'Product-side metadata (free-form JSON)',
4158
4957
  example: {
4159
- narration: true,
4160
- payee: false,
4161
- categoryAccount: false
4958
+ note: 'Promotion'
4162
4959
  }
4960
+ },
4961
+ createdAt: {
4962
+ format: 'date-time',
4963
+ type: 'string',
4964
+ description: 'Creation timestamp',
4965
+ example: '2024-03-15T10:00:00Z'
4966
+ },
4967
+ updatedAt: {
4968
+ format: 'date-time',
4969
+ type: 'string',
4970
+ description:
4971
+ 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
4972
+ example: '2024-03-15T10:00:00Z'
4163
4973
  }
4164
4974
  },
4165
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
4975
+ required: [
4976
+ 'id',
4977
+ 'userId',
4978
+ 'date',
4979
+ 'type',
4980
+ 'description',
4981
+ 'meta',
4982
+ 'createdAt',
4983
+ 'updatedAt'
4984
+ ]
4166
4985
  } as const;
4167
4986
 
4168
- export const $DeleteOwnUserDto = {
4987
+ export const $EventListResponseDto = {
4169
4988
  type: 'object',
4170
4989
  properties: {
4171
- accessToken: {
4172
- type: 'string',
4173
- description: 'Access token for user verification',
4174
- example: 'abc123xyz'
4990
+ items: {
4991
+ description: 'List of life events',
4992
+ type: 'array',
4993
+ items: {
4994
+ $ref: '#/components/schemas/EventResponseDto'
4995
+ }
4996
+ },
4997
+ total: {
4998
+ type: 'number',
4999
+ description: 'Total number of life events matching the query',
5000
+ example: 42
4175
5001
  }
4176
5002
  },
4177
- required: ['accessToken']
5003
+ required: ['items', 'total']
4178
5004
  } as const;
4179
5005
 
4180
- export const $SignupDto = {
5006
+ export const $UpdateBeanEventDto = {
4181
5007
  type: 'object',
4182
5008
  properties: {
4183
- turnstileToken: {
5009
+ date: {
5010
+ type: 'string',
5011
+ description: 'Life event date (ISO 8601)'
5012
+ },
5013
+ type: {
5014
+ type: 'string',
5015
+ description: 'Life event type (user-defined)'
5016
+ },
5017
+ description: {
4184
5018
  type: 'string',
4185
5019
  description:
4186
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4187
- example: '0.abc123def456...'
5020
+ 'Life event description. Empty string is a VALID value (distinct from absence).'
5021
+ },
5022
+ meta: {
5023
+ type: 'object',
5024
+ description: 'Product-side metadata (free-form JSON)'
4188
5025
  }
4189
5026
  }
4190
5027
  } as const;
4191
5028
 
4192
- export const $UpdateUserSettingDto = {
5029
+ export const $OnboardingAccountDto = {
4193
5030
  type: 'object',
4194
5031
  properties: {
4195
- secId: {
4196
- type: 'number',
4197
- description: 'Security ID'
4198
- },
4199
- annualInterestRate: {
4200
- type: 'number',
4201
- description: 'Annual interest rate',
4202
- example: 0.05
4203
- },
4204
- currency: {
5032
+ path: {
4205
5033
  type: 'string',
4206
- description: 'Currency code',
4207
- example: 'USD'
5034
+ description:
5035
+ 'Account path (Assets/Liabilities only; format validated by the account service)',
5036
+ example: 'Assets:Checking'
4208
5037
  },
4209
- baseCurrency: {
5038
+ currency: {
4210
5039
  type: 'string',
4211
- description: 'Base currency code',
5040
+ description: 'ISO 4217 currency code (3 letters)',
4212
5041
  example: 'USD'
4213
5042
  },
4214
- benchmark: {
4215
- type: 'string',
4216
- description: 'Benchmark symbol',
4217
- example: 'SPY'
4218
- },
4219
- colorScheme: {
4220
- type: 'string',
4221
- description: 'Color scheme',
4222
- enum: ['DARK', 'LIGHT']
4223
- },
4224
- dateRange: {
4225
- type: 'string',
4226
- description: 'Date range filter',
4227
- example: '1y'
4228
- },
4229
- emergencyFund: {
4230
- type: 'number',
4231
- description: 'Emergency fund amount',
4232
- example: 10000
4233
- },
4234
- 'filters.accounts': {
4235
- description: 'Account filter IDs',
4236
- type: 'array',
4237
- items: {
4238
- type: 'string'
4239
- }
4240
- },
4241
- 'filters.assetClasses': {
4242
- description: 'Asset class filters',
4243
- type: 'array',
4244
- items: {
4245
- type: 'string'
4246
- }
4247
- },
4248
- 'filters.dataSource': {
5043
+ openingBalance: {
4249
5044
  type: 'string',
4250
- description: 'Data source filter'
5045
+ description:
5046
+ 'Opening balance as a non-negative Decimal string (e.g. "1000.00")',
5047
+ example: '1000.00'
4251
5048
  },
4252
- 'filters.symbol': {
5049
+ platformId: {
4253
5050
  type: 'string',
4254
- description: 'Symbol filter'
4255
- },
4256
- 'filters.tags': {
4257
- description: 'Tag filters',
5051
+ description:
5052
+ 'Platform ID to bind the account to (references Platform.id); omit for unbound',
5053
+ example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
5054
+ }
5055
+ },
5056
+ required: ['path', 'currency']
5057
+ } as const;
5058
+
5059
+ export const $OnboardingDto = {
5060
+ type: 'object',
5061
+ properties: {
5062
+ accounts: {
5063
+ description: 'Asset/Liability accounts to register with opening balances',
4258
5064
  type: 'array',
4259
5065
  items: {
4260
- type: 'string'
5066
+ $ref: '#/components/schemas/OnboardingAccountDto'
4261
5067
  }
4262
5068
  },
4263
- isExperimentalFeatures: {
4264
- type: 'boolean',
4265
- description: 'Enable experimental features'
4266
- },
4267
- isRestrictedView: {
5069
+ skipAssetRegistration: {
4268
5070
  type: 'boolean',
4269
- description: 'Enable restricted view mode'
4270
- },
4271
- language: {
5071
+ description:
5072
+ 'Skip asset registration; only bootstrap the core account set',
5073
+ default: false
5074
+ }
5075
+ }
5076
+ } as const;
5077
+
5078
+ export const $ActualBalanceDto = {
5079
+ type: 'object',
5080
+ properties: {
5081
+ amount: {
4272
5082
  type: 'string',
4273
- description: 'Language code',
4274
- example: 'en'
5083
+ description:
5084
+ 'Actual balance amount as a decimal string (preserves precision for tolerance inference).',
5085
+ example: '1234.56'
4275
5086
  },
4276
- locale: {
5087
+ ccy: {
4277
5088
  type: 'string',
4278
- description: 'Locale code',
4279
- example: 'en-US'
4280
- },
4281
- projectedTotalAmount: {
4282
- type: 'number',
4283
- description: 'Projected total amount',
4284
- example: 1000000
4285
- },
4286
- retirementDate: {
5089
+ description: 'Currency code (ISO 4217 or commodity ticker).',
5090
+ example: 'CNY'
5091
+ }
5092
+ },
5093
+ required: ['amount', 'ccy']
5094
+ } as const;
5095
+
5096
+ export const $ComputeReconciliationDto = {
5097
+ type: 'object',
5098
+ properties: {
5099
+ accountId: {
4287
5100
  type: 'string',
4288
- description: 'Retirement date in ISO 8601 format',
4289
- example: '2050-01-01'
4290
- },
4291
- savingsRate: {
4292
- type: 'number',
4293
- description: 'Savings rate percentage',
4294
- example: 0.2
5101
+ description: 'BeanAccount id to reconcile.'
4295
5102
  },
4296
- viewMode: {
5103
+ asOfDate: {
4297
5104
  type: 'string',
4298
- description: 'View mode',
4299
- enum: ['DEFAULT', 'ZEN']
5105
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5106
+ example: '2026-07-24'
5107
+ },
5108
+ actualBalance: {
5109
+ description: 'Actual balance from the external statement.',
5110
+ allOf: [
5111
+ {
5112
+ $ref: '#/components/schemas/ActualBalanceDto'
5113
+ }
5114
+ ]
4300
5115
  }
4301
- }
5116
+ },
5117
+ required: ['accountId', 'asOfDate', 'actualBalance']
4302
5118
  } as const;
4303
5119
 
4304
- export const $UpdatePropertyDto = {
5120
+ export const $ReconciliationComputeResultDto = {
4305
5121
  type: 'object',
4306
5122
  properties: {
4307
- value: {
5123
+ accountId: {
5124
+ type: 'string'
5125
+ },
5126
+ asOfDate: {
5127
+ type: 'string'
5128
+ },
5129
+ bookBalance: {
5130
+ type: 'string',
5131
+ description: 'System-computed book balance (decimal string).'
5132
+ },
5133
+ actualBalance: {
5134
+ type: 'string',
5135
+ description: 'User-entered actual balance (decimal string).'
5136
+ },
5137
+ currency: {
5138
+ type: 'string'
5139
+ },
5140
+ diff: {
4308
5141
  type: 'string',
4309
- description: 'Property value'
5142
+ description: 'Diff = book − actual (decimal string).'
5143
+ },
5144
+ tolerance: {
5145
+ type: 'string',
5146
+ description: 'Applied tolerance (decimal string).'
5147
+ },
5148
+ withinTolerance: {
5149
+ type: 'boolean',
5150
+ description: 'true when |diff| ≤ tolerance.'
5151
+ },
5152
+ suggestedAction: {
5153
+ type: 'string',
5154
+ enum: ['assert', 'pad'],
5155
+ description:
5156
+ 'Suggested next action: assert when within tolerance, pad otherwise.'
4310
5157
  }
4311
5158
  },
4312
- required: ['value']
5159
+ required: [
5160
+ 'accountId',
5161
+ 'asOfDate',
5162
+ 'bookBalance',
5163
+ 'actualBalance',
5164
+ 'currency',
5165
+ 'diff',
5166
+ 'tolerance',
5167
+ 'withinTolerance',
5168
+ 'suggestedAction'
5169
+ ]
4313
5170
  } as const;
4314
5171
 
4315
- export const $CreateBeanEventDto = {
5172
+ export const $AssertReconciliationDto = {
4316
5173
  type: 'object',
4317
5174
  properties: {
4318
- date: {
5175
+ accountId: {
4319
5176
  type: 'string',
4320
- description: 'Life event date (ISO 8601)',
4321
- example: '2024-03-15'
5177
+ description: 'BeanAccount id to reconcile.'
4322
5178
  },
4323
- type: {
5179
+ asOfDate: {
4324
5180
  type: 'string',
4325
- description:
4326
- 'Life event type (e.g., "employer", "location", "marital-status") — user-defined, no enum constraint at engine layer',
4327
- example: 'employer'
5181
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5182
+ example: '2026-07-24'
4328
5183
  },
4329
- description: {
4330
- type: 'string',
4331
- description:
4332
- 'Life event description. Empty string is a VALID value (distinct from absence).',
4333
- example: 'Acme Corp'
5184
+ actualBalance: {
5185
+ description: 'Actual balance from the external statement.',
5186
+ allOf: [
5187
+ {
5188
+ $ref: '#/components/schemas/ActualBalanceDto'
5189
+ }
5190
+ ]
4334
5191
  },
4335
- meta: {
4336
- type: 'object',
5192
+ tolerance: {
5193
+ type: 'string',
4337
5194
  description:
4338
- 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
4339
- example: {
4340
- note: 'Promotion'
4341
- }
5195
+ 'Optional explicit tolerance override. Omit to infer from amount precision (Beancount default).',
5196
+ example: '0.01'
4342
5197
  }
4343
5198
  },
4344
- required: ['date', 'type', 'description']
5199
+ required: ['accountId', 'asOfDate', 'actualBalance']
4345
5200
  } as const;
4346
5201
 
4347
- export const $EventResponseDto = {
5202
+ export const $ReconciliationRecordDto = {
4348
5203
  type: 'object',
4349
5204
  properties: {
4350
5205
  id: {
4351
- type: 'string',
4352
- description: 'Unique identifier',
4353
- example: 'uuid-123-456'
5206
+ type: 'string'
4354
5207
  },
4355
- userId: {
4356
- type: 'string',
4357
- description: 'User ID (owner of the life event)',
4358
- example: 'user-123'
5208
+ accountId: {
5209
+ type: 'string'
4359
5210
  },
4360
5211
  date: {
4361
- type: 'string',
4362
- description: 'Life event date (ISO 8601 format)',
4363
- example: '2024-03-15',
4364
- format: 'date'
5212
+ type: 'string'
4365
5213
  },
4366
- type: {
5214
+ amount: {
4367
5215
  type: 'string',
4368
- description:
4369
- 'Life event type (user-defined, e.g., "employer", "location")',
4370
- example: 'employer'
5216
+ description: 'Asserted (actual) amount.'
4371
5217
  },
4372
- description: {
4373
- type: 'string',
4374
- description:
4375
- 'Life event description. May be an empty string (a valid value distinct from absence).',
4376
- example: 'Acme Corp'
5218
+ currency: {
5219
+ type: 'string'
4377
5220
  },
4378
- meta: {
4379
- type: 'object',
4380
- description: 'Product-side metadata (free-form JSON)',
4381
- example: {
4382
- note: 'Promotion'
4383
- }
5221
+ tolerance: {
5222
+ type: 'string'
4384
5223
  },
4385
- createdAt: {
4386
- format: 'date-time',
5224
+ diffAmount: {
4387
5225
  type: 'string',
4388
- description: 'Creation timestamp',
4389
- example: '2024-03-15T10:00:00Z'
5226
+ description: 'book − actual.'
4390
5227
  },
4391
- updatedAt: {
4392
- format: 'date-time',
4393
- type: 'string',
4394
- description:
4395
- 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
4396
- example: '2024-03-15T10:00:00Z'
5228
+ diffCurrency: {
5229
+ type: 'string'
5230
+ },
5231
+ createdAt: {
5232
+ type: 'string'
4397
5233
  }
4398
5234
  },
4399
- required: [
4400
- 'id',
4401
- 'userId',
4402
- 'date',
4403
- 'type',
4404
- 'description',
4405
- 'meta',
4406
- 'createdAt',
4407
- 'updatedAt'
4408
- ]
5235
+ required: ['id', 'accountId', 'date', 'amount', 'currency', 'createdAt']
4409
5236
  } as const;
4410
5237
 
4411
- export const $EventListResponseDto = {
5238
+ export const $PadReconciliationDto = {
4412
5239
  type: 'object',
4413
5240
  properties: {
4414
- items: {
4415
- description: 'List of life events',
4416
- type: 'array',
4417
- items: {
4418
- $ref: '#/components/schemas/EventResponseDto'
4419
- }
5241
+ accountId: {
5242
+ type: 'string',
5243
+ description: 'BeanAccount id to reconcile.'
4420
5244
  },
4421
- total: {
4422
- type: 'number',
4423
- description: 'Total number of life events matching the query',
4424
- example: 42
5245
+ asOfDate: {
5246
+ type: 'string',
5247
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5248
+ example: '2026-07-24'
5249
+ },
5250
+ actualBalance: {
5251
+ description: 'Actual balance from the external statement.',
5252
+ allOf: [
5253
+ {
5254
+ $ref: '#/components/schemas/ActualBalanceDto'
5255
+ }
5256
+ ]
5257
+ },
5258
+ sourceAccount: {
5259
+ type: 'string',
5260
+ description:
5261
+ 'Pad source account. Defaults to Equity:Opening-Balances (official Beancount convention).',
5262
+ example: 'Equity:Opening-Balances',
5263
+ default: 'Equity:Opening-Balances'
4425
5264
  }
4426
5265
  },
4427
- required: ['items', 'total']
5266
+ required: ['accountId', 'asOfDate', 'actualBalance']
4428
5267
  } as const;
4429
5268
 
4430
- export const $UpdateBeanEventDto = {
5269
+ export const $PadResultDto = {
4431
5270
  type: 'object',
4432
5271
  properties: {
4433
- date: {
4434
- type: 'string',
4435
- description: 'Life event date (ISO 8601)'
4436
- },
4437
- type: {
4438
- type: 'string',
4439
- description: 'Life event type (user-defined)'
4440
- },
4441
- description: {
5272
+ transactionId: {
4442
5273
  type: 'string',
4443
- description:
4444
- 'Life event description. Empty string is a VALID value (distinct from absence).'
4445
- },
4446
- meta: {
4447
- type: 'object',
4448
- description: 'Product-side metadata (free-form JSON)'
5274
+ description: 'Created pad adjusting transaction id.'
4449
5275
  }
4450
- }
5276
+ },
5277
+ required: ['transactionId']
4451
5278
  } as const;
4452
5279
 
4453
5280
  export const $FileImportDto = {
@@ -4616,7 +5443,7 @@ export const $IdentifyResultDto = {
4616
5443
  account: {
4617
5444
  type: 'string',
4618
5445
  description: 'Default account used by this importer',
4619
- example: 'Assets:Alipay:Balance'
5446
+ example: 'Assets:CN:Alipay:Balance'
4620
5447
  },
4621
5448
  message: {
4622
5449
  type: 'string',
@@ -4633,7 +5460,7 @@ export const $MapperDefaultsDto = {
4633
5460
  sourceAccount: {
4634
5461
  type: 'string',
4635
5462
  description: 'Source account for transactions (Beancount format)',
4636
- example: 'Assets:Alipay:Balance'
5463
+ example: 'Assets:CN:Alipay:Balance'
4637
5464
  },
4638
5465
  currency: {
4639
5466
  type: 'string',
@@ -4670,7 +5497,7 @@ export const $MapperDefaultsDto = {
4670
5497
  description:
4671
5498
  'Payment method to source account mapping. Maps payment method keywords to Beancount account paths. Used by Alipay/WeChat importers to determine sourceAccount based on payment method (e.g., HuaBei, CreditCard).',
4672
5499
  example: {
4673
- HuaBei: 'Liabilities:Alipay:Huabei',
5500
+ HuaBei: 'Liabilities:CN:CreditLine',
4674
5501
  CreditCard: 'Liabilities:CreditCard'
4675
5502
  }
4676
5503
  }
@@ -4802,7 +5629,7 @@ export const $UpdateMapperDefaultsDto = {
4802
5629
  sourceAccount: {
4803
5630
  type: 'string',
4804
5631
  description: 'Source account for transactions (Beancount format)',
4805
- example: 'Assets:Alipay:Balance',
5632
+ example: 'Assets:CN:Alipay:Balance',
4806
5633
  pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
4807
5634
  },
4808
5635
  currency: {
@@ -4830,7 +5657,7 @@ export const $UpdateMapperDefaultsDto = {
4830
5657
  description:
4831
5658
  'Payment method to source account mapping. Maps payment method keywords to Beancount account paths. Used by Alipay/WeChat importers to determine sourceAccount based on payment method (e.g., HuaBei, CreditCard).',
4832
5659
  example: {
4833
- HuaBei: 'Liabilities:Alipay:Huabei',
5660
+ HuaBei: 'Liabilities:CN:CreditLine',
4834
5661
  CreditCard: 'Liabilities:CreditCard'
4835
5662
  }
4836
5663
  }
@@ -4855,118 +5682,12 @@ export const $UpdateImporterConfigDto = {
4855
5682
  type: 'object',
4856
5683
  properties: {
4857
5684
  data: {
4858
- description: 'Configuration data (v1 schema)',
4859
- allOf: [
4860
- {
4861
- $ref: '#/components/schemas/UpdateConfigDataDto'
4862
- }
4863
- ]
4864
- }
4865
- }
4866
- } as const;
4867
-
4868
- export const $CreatePlatformDto = {
4869
- type: 'object',
4870
- properties: {
4871
- name: {
4872
- type: 'string',
4873
- description: 'Platform name',
4874
- example: 'Binance'
4875
- },
4876
- canonical: {
4877
- type: 'string',
4878
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4879
- example: 'binance'
4880
- },
4881
- aliases: {
4882
- description: 'Platform aliases (multi-language names for lookup)',
4883
- example: ['Binance', 'Binance Exchange', 'BNB'],
4884
- type: 'array',
4885
- items: {
4886
- type: 'string'
4887
- }
4888
- },
4889
- url: {
4890
- type: 'string',
4891
- description: 'Platform URL',
4892
- example: 'https://www.binance.com'
4893
- },
4894
- type: {
4895
- type: 'string',
4896
- description: 'Platform type',
4897
- enum: [
4898
- 'BANK',
4899
- 'BROKERAGE',
4900
- 'CRYPTO_EXCHANGE',
4901
- 'PAYMENT',
4902
- 'INVESTMENT',
4903
- 'INSURANCE',
4904
- 'OTHER'
4905
- ],
4906
- example: 'CRYPTO_EXCHANGE'
4907
- },
4908
- logoUrl: {
4909
- type: 'string',
4910
- description: 'Platform logo URL',
4911
- example: 'https://example.com/logos/binance.png'
4912
- },
4913
- isActive: {
4914
- type: 'boolean',
4915
- description: 'Whether the platform is active',
4916
- default: true
4917
- }
4918
- },
4919
- required: ['name', 'canonical', 'aliases', 'url', 'type']
4920
- } as const;
4921
-
4922
- export const $UpdatePlatformDto = {
4923
- type: 'object',
4924
- properties: {
4925
- name: {
4926
- type: 'string',
4927
- description: 'Platform name',
4928
- example: 'Binance'
4929
- },
4930
- canonical: {
4931
- type: 'string',
4932
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4933
- example: 'binance'
4934
- },
4935
- aliases: {
4936
- description: 'Platform aliases (multi-language names for lookup)',
4937
- example: ['Binance', 'Binance Exchange', 'BNB'],
4938
- type: 'array',
4939
- items: {
4940
- type: 'string'
4941
- }
4942
- },
4943
- url: {
4944
- type: 'string',
4945
- description: 'Platform URL',
4946
- example: 'https://www.binance.com'
4947
- },
4948
- type: {
4949
- type: 'string',
4950
- description: 'Platform type',
4951
- enum: [
4952
- 'BANK',
4953
- 'BROKERAGE',
4954
- 'CRYPTO_EXCHANGE',
4955
- 'PAYMENT',
4956
- 'INVESTMENT',
4957
- 'INSURANCE',
4958
- 'OTHER'
4959
- ],
4960
- example: 'CRYPTO_EXCHANGE'
4961
- },
4962
- logoUrl: {
4963
- type: 'string',
4964
- description: 'Platform logo URL',
4965
- example: 'https://example.com/logos/binance.png'
4966
- },
4967
- isActive: {
4968
- type: 'boolean',
4969
- description: 'Whether the platform is active'
5685
+ description: 'Configuration data (v1 schema)',
5686
+ allOf: [
5687
+ {
5688
+ $ref: '#/components/schemas/UpdateConfigDataDto'
5689
+ }
5690
+ ]
4970
5691
  }
4971
5692
  }
4972
5693
  } as const;
@@ -4977,7 +5698,7 @@ export const $ProviderSyncConfigDto = {
4977
5698
  sourceAccount: {
4978
5699
  type: 'string',
4979
5700
  description: 'Source account for the first posting',
4980
- example: 'Assets:Bank:Chase'
5701
+ example: 'Assets:US:Chase:Checking'
4981
5702
  },
4982
5703
  defaultCurrency: {
4983
5704
  type: 'string',
@@ -4998,6 +5719,12 @@ export const $ProviderSyncConfigDto = {
4998
5719
  type: 'boolean',
4999
5720
  description: 'Filter pending transactions',
5000
5721
  default: true
5722
+ },
5723
+ externalAccountId: {
5724
+ type: 'string',
5725
+ description:
5726
+ 'External account ID for per-batch providers (e.g. GoCardless). Overrides sourceAccount when an ExternalAccountLink mapping exists.',
5727
+ example: 'acc_gocardless_001'
5001
5728
  }
5002
5729
  },
5003
5730
  required: [
@@ -5117,6 +5844,94 @@ export const $SupportedProvidersResponseDto = {
5117
5844
  required: ['providers']
5118
5845
  } as const;
5119
5846
 
5847
+ export const $CreateExternalAccountLinkDto = {
5848
+ type: 'object',
5849
+ properties: {
5850
+ provider: {
5851
+ type: 'string',
5852
+ enum: [
5853
+ 'plaid',
5854
+ 'teller',
5855
+ 'truelayer',
5856
+ 'gocardless',
5857
+ 'simplefin',
5858
+ 'yodlee',
5859
+ 'beancount-direct',
5860
+ 'parsed-bill'
5861
+ ],
5862
+ example: 'plaid',
5863
+ description: 'Open Banking provider (whitelist)'
5864
+ },
5865
+ externalAccountId: {
5866
+ type: 'string',
5867
+ example: 'acc-plaid-001',
5868
+ description: 'External account ID from the provider'
5869
+ },
5870
+ beanAccountId: {
5871
+ type: 'string',
5872
+ example: '550e8400-e29b-41d4-a716-446655440000',
5873
+ description: 'Target BeanAccount ID (must belong to the JWT user)'
5874
+ }
5875
+ },
5876
+ required: ['provider', 'externalAccountId', 'beanAccountId']
5877
+ } as const;
5878
+
5879
+ export const $ExternalAccountLinkResponseDto = {
5880
+ type: 'object',
5881
+ properties: {
5882
+ id: {
5883
+ type: 'string'
5884
+ },
5885
+ provider: {
5886
+ type: 'string'
5887
+ },
5888
+ externalAccountId: {
5889
+ type: 'string'
5890
+ },
5891
+ beanAccountId: {
5892
+ type: 'string'
5893
+ },
5894
+ isActive: {
5895
+ type: 'boolean'
5896
+ },
5897
+ createdAt: {
5898
+ type: 'string'
5899
+ },
5900
+ updatedAt: {
5901
+ type: 'string'
5902
+ }
5903
+ },
5904
+ required: [
5905
+ 'id',
5906
+ 'provider',
5907
+ 'externalAccountId',
5908
+ 'beanAccountId',
5909
+ 'isActive',
5910
+ 'createdAt',
5911
+ 'updatedAt'
5912
+ ]
5913
+ } as const;
5914
+
5915
+ export const $ExternalAccountLinkListResponseDto = {
5916
+ type: 'object',
5917
+ properties: {
5918
+ items: {
5919
+ type: 'array',
5920
+ items: {
5921
+ $ref: '#/components/schemas/ExternalAccountLinkResponseDto'
5922
+ }
5923
+ },
5924
+ total: {
5925
+ type: 'number'
5926
+ },
5927
+ provider: {
5928
+ type: 'string',
5929
+ description: 'Filter by provider (query param)'
5930
+ }
5931
+ },
5932
+ required: ['items', 'total']
5933
+ } as const;
5934
+
5120
5935
  export const $ParserTelemetryReportDto = {
5121
5936
  type: 'object',
5122
5937
  properties: {}
@@ -5151,6 +5966,18 @@ export const $ProcessNlpDto = {
5151
5966
  currency: 'CNY',
5152
5967
  payee: 'Starbucks'
5153
5968
  }
5969
+ },
5970
+ selectedRuleId: {
5971
+ type: 'string',
5972
+ description:
5973
+ '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.',
5974
+ example: 'rule_abc123'
5975
+ },
5976
+ selectedAccount: {
5977
+ type: 'string',
5978
+ description:
5979
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i], or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
5980
+ example: 'Expenses:Food:Coffee'
5154
5981
  }
5155
5982
  },
5156
5983
  required: ['message']
@@ -5475,7 +6302,8 @@ export const $NlpAccountConfirmationDataDto = {
5475
6302
  },
5476
6303
  suggestedAccount: {
5477
6304
  type: 'string',
5478
- description: 'Suggested replacement account',
6305
+ description:
6306
+ 'Suggested replacement account (omitted when no clear candidate)',
5479
6307
  example: 'Expenses:Food:Drinks'
5480
6308
  },
5481
6309
  similarAccounts: {
@@ -5497,7 +6325,6 @@ export const $NlpAccountConfirmationDataDto = {
5497
6325
  },
5498
6326
  required: [
5499
6327
  'invalidAccount',
5500
- 'suggestedAccount',
5501
6328
  'similarAccounts',
5502
6329
  'errorMessage',
5503
6330
  'transactionContext'
@@ -5666,7 +6493,7 @@ export const $NlpSuggestedAccountDto = {
5666
6493
  account: {
5667
6494
  type: 'string',
5668
6495
  description: 'Suggested account path',
5669
- example: 'Assets:Bank:Checking'
6496
+ example: 'Assets:Checking'
5670
6497
  },
5671
6498
  confidence: {
5672
6499
  type: 'number',
@@ -5707,7 +6534,7 @@ export const $NlpDefaultAccountsDto = {
5707
6534
  asset: {
5708
6535
  type: 'string',
5709
6536
  description: 'Default asset account',
5710
- example: 'Assets:Bank:Checking'
6537
+ example: 'Assets:Checking'
5711
6538
  },
5712
6539
  expense: {
5713
6540
  type: 'string',
@@ -5747,7 +6574,8 @@ export const $NlpResponseDto = {
5747
6574
  'confirm_rule',
5748
6575
  'confirm_account',
5749
6576
  'confirm_payee',
5750
- 'cancel'
6577
+ 'cancel',
6578
+ 'aborted'
5751
6579
  ]
5752
6580
  },
5753
6581
  intent: {
@@ -5907,1443 +6735,1526 @@ export const $NlpResponseDto = {
5907
6735
  }
5908
6736
  ]
5909
6737
  },
5910
- suggestedAccounts: {
5911
- description:
5912
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
5913
- allOf: [
5914
- {
5915
- $ref: '#/components/schemas/NlpSuggestedAccountsDto'
5916
- }
5917
- ]
5918
- },
5919
- defaultAccounts: {
5920
- description:
5921
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
5922
- allOf: [
5923
- {
5924
- $ref: '#/components/schemas/NlpDefaultAccountsDto'
5925
- }
5926
- ]
5927
- }
5928
- },
5929
- required: ['status', 'action']
5930
- } as const;
5931
-
5932
- export const $BalanceByCurrencyDto = {
5933
- type: 'object',
5934
- properties: {
5935
- currency: {
5936
- type: 'string',
5937
- description: 'ISO 4217 currency code',
5938
- example: 'CNY'
5939
- },
5940
- balance: {
5941
- type: 'string',
5942
- description: 'Balance amount',
5943
- example: '50000.00'
5944
- }
5945
- },
5946
- required: ['currency', 'balance']
5947
- } as const;
5948
-
5949
- export const $NetWorthByCurrencyDto = {
5950
- type: 'object',
5951
- properties: {
5952
- netWorth: {
5953
- description: 'Net worth by currency',
5954
- type: 'array',
5955
- items: {
5956
- $ref: '#/components/schemas/BalanceByCurrencyDto'
5957
- }
5958
- },
5959
- assets: {
5960
- description: 'Assets by currency',
5961
- type: 'array',
5962
- items: {
5963
- $ref: '#/components/schemas/BalanceByCurrencyDto'
5964
- }
5965
- },
5966
- liabilities: {
5967
- description: 'Liabilities by currency',
5968
- type: 'array',
5969
- items: {
5970
- $ref: '#/components/schemas/BalanceByCurrencyDto'
5971
- }
5972
- }
5973
- },
5974
- required: ['netWorth', 'assets', 'liabilities']
5975
- } as const;
5976
-
5977
- export const $ConvertedNetWorthDto = {
5978
- type: 'object',
5979
- properties: {
5980
- baseCurrency: {
5981
- type: 'string',
5982
- description: 'Base currency for conversion',
5983
- example: 'CNY'
5984
- },
5985
- netWorth: {
5986
- type: 'string',
5987
- description: 'Converted net worth',
5988
- example: '500000.00'
5989
- },
5990
- assets: {
5991
- type: 'string',
5992
- description: 'Converted assets',
5993
- example: '600000.00'
5994
- },
5995
- liabilities: {
5996
- type: 'string',
5997
- description: 'Converted liabilities',
5998
- example: '100000.00'
5999
- },
6000
- exchangeRates: {
6001
- type: 'object',
6002
- description: 'Exchange rates used for conversion',
6003
- example: {
6004
- USD: '7.200000',
6005
- EUR: '7.800000'
6006
- }
6007
- }
6008
- },
6009
- required: [
6010
- 'baseCurrency',
6011
- 'netWorth',
6012
- 'assets',
6013
- 'liabilities',
6014
- 'exchangeRates'
6015
- ]
6016
- } as const;
6017
-
6018
- export const $ExchangeRateWarningDto = {
6019
- type: 'object',
6020
- properties: {
6021
- type: {
6022
- type: 'string',
6023
- description: 'Warning type',
6024
- example: 'MISSING_EXCHANGE_RATE'
6025
- },
6026
- currency: {
6027
- type: 'string',
6028
- description: 'Currency without exchange rate',
6029
- example: 'EUR'
6030
- },
6031
- totalAmount: {
6032
- type: 'string',
6033
- description: 'Total amount affected',
6034
- example: '1000.00'
6035
- }
6036
- },
6037
- required: ['type', 'currency', 'totalAmount']
6038
- } as const;
6039
-
6040
- export const $NetWorthResponseDto = {
6041
- type: 'object',
6042
- properties: {
6043
- netWorth: {
6044
- type: 'string',
6045
- description:
6046
- 'Total net worth (assets - liabilities, converted to base currency)',
6047
- example: '500000.00'
6048
- },
6049
- assets: {
6050
- type: 'string',
6051
- description: 'Total assets value (converted)',
6052
- example: '600000.00'
6053
- },
6054
- liabilities: {
6055
- type: 'string',
6056
- description: 'Total liabilities value (positive number, converted)',
6057
- example: '100000.00'
6058
- },
6059
- monthlyReturn: {
6060
- type: 'string',
6061
- description: 'Monthly return (change from last month)',
6062
- example: '15000.00'
6063
- },
6064
- monthlyReturnPercentage: {
6065
- type: 'string',
6066
- description: 'Monthly return percentage',
6067
- example: '3.15'
6068
- },
6069
- currency: {
6070
- type: 'string',
6071
- description: 'Base currency code',
6072
- example: 'CNY'
6073
- },
6074
- asOf: {
6075
- type: 'string',
6076
- description: 'Data as of date (ISO 8601)',
6077
- example: '2024-06-15T00:00:00.000Z'
6078
- },
6079
- byCurrency: {
6080
- description: 'Balances grouped by original currency',
6738
+ suggestedAccounts: {
6739
+ description:
6740
+ 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
6081
6741
  allOf: [
6082
6742
  {
6083
- $ref: '#/components/schemas/NetWorthByCurrencyDto'
6743
+ $ref: '#/components/schemas/NlpSuggestedAccountsDto'
6084
6744
  }
6085
6745
  ]
6086
6746
  },
6087
- converted: {
6747
+ defaultAccounts: {
6088
6748
  description:
6089
- 'Converted values in base currency (undefined if no exchange rates available)',
6749
+ 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
6090
6750
  allOf: [
6091
6751
  {
6092
- $ref: '#/components/schemas/ConvertedNetWorthDto'
6752
+ $ref: '#/components/schemas/NlpDefaultAccountsDto'
6093
6753
  }
6094
6754
  ]
6095
- },
6096
- warnings: {
6097
- description: 'Exchange rate warnings',
6098
- type: 'array',
6099
- items: {
6100
- $ref: '#/components/schemas/ExchangeRateWarningDto'
6101
- }
6102
6755
  }
6103
6756
  },
6104
- required: [
6105
- 'netWorth',
6106
- 'assets',
6107
- 'liabilities',
6108
- 'monthlyReturn',
6109
- 'monthlyReturnPercentage',
6110
- 'currency',
6111
- 'asOf'
6112
- ]
6757
+ required: ['status', 'action']
6113
6758
  } as const;
6114
6759
 
6115
- export const $AccountItemDto = {
6760
+ export const $PlatformListItemDto = {
6116
6761
  type: 'object',
6117
6762
  properties: {
6118
6763
  id: {
6119
6764
  type: 'string',
6120
- description: 'Account ID'
6765
+ description: 'Global platform ID'
6121
6766
  },
6122
6767
  name: {
6123
6768
  type: 'string',
6124
- description: 'Full account name',
6125
- example: 'Assets:Bank:CMB:Savings'
6769
+ description: 'Platform name'
6126
6770
  },
6127
- displayName: {
6771
+ url: {
6128
6772
  type: 'string',
6129
- description: 'Display name (last part of account path)',
6130
- example: 'Savings'
6773
+ description: 'Platform URL'
6131
6774
  },
6132
- balance: {
6775
+ type: {
6133
6776
  type: 'string',
6134
- description: 'Account balance',
6135
- example: '50000.00'
6777
+ description: 'Platform type',
6778
+ enum: [
6779
+ 'BANK',
6780
+ 'BROKERAGE',
6781
+ 'CRYPTO_EXCHANGE',
6782
+ 'PAYMENT',
6783
+ 'INVESTMENT',
6784
+ 'INSURANCE',
6785
+ 'OTHER'
6786
+ ]
6136
6787
  },
6137
- currency: {
6788
+ canonical: {
6138
6789
  type: 'string',
6139
- description: 'Currency code',
6140
- example: 'CNY'
6790
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
6791
+ },
6792
+ suggestedSegment: {
6793
+ type: 'string',
6794
+ description:
6795
+ 'Suggested path segment — canonical with first char uppercased (ACC_COMP_NAME_RE)'
6796
+ },
6797
+ logoUrl: {
6798
+ type: 'string',
6799
+ description: 'Logo URL',
6800
+ nullable: true
6801
+ },
6802
+ isBound: {
6803
+ type: 'boolean',
6804
+ description: 'Whether user has accounts using this platform'
6141
6805
  }
6142
6806
  },
6143
- required: ['id', 'name', 'displayName', 'balance', 'currency']
6807
+ required: [
6808
+ 'id',
6809
+ 'name',
6810
+ 'url',
6811
+ 'type',
6812
+ 'canonical',
6813
+ 'suggestedSegment',
6814
+ 'logoUrl',
6815
+ 'isBound'
6816
+ ]
6144
6817
  } as const;
6145
6818
 
6146
- export const $PlatformGroupDto = {
6819
+ export const $PlatformMatchResultDto = {
6147
6820
  type: 'object',
6148
6821
  properties: {
6149
- platformId: {
6822
+ id: {
6150
6823
  type: 'string',
6151
- description: 'Platform ID'
6824
+ description: 'Global platform ID'
6152
6825
  },
6153
- platformName: {
6826
+ name: {
6154
6827
  type: 'string',
6155
- description: 'Platform display name',
6156
- example: 'CMB Bank'
6828
+ description: 'Platform name (e.g., "ICBC")'
6157
6829
  },
6158
- accounts: {
6159
- description: 'Accounts within this platform',
6160
- type: 'array',
6161
- items: {
6162
- $ref: '#/components/schemas/AccountItemDto'
6163
- }
6830
+ canonical: {
6831
+ type: 'string',
6832
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
6164
6833
  },
6165
- totalBalance: {
6834
+ type: {
6166
6835
  type: 'string',
6167
- description: 'Total balance across all accounts in platform',
6168
- example: '100000.00'
6169
- }
6170
- },
6171
- required: ['platformId', 'platformName', 'accounts', 'totalBalance']
6172
- } as const;
6173
-
6174
- export const $AccountsSummaryDto = {
6175
- type: 'object',
6176
- properties: {
6177
- totalAccounts: {
6178
- type: 'number',
6179
- description: 'Total number of accounts'
6836
+ description: 'Platform type',
6837
+ enum: [
6838
+ 'BANK',
6839
+ 'BROKERAGE',
6840
+ 'CRYPTO_EXCHANGE',
6841
+ 'PAYMENT',
6842
+ 'INVESTMENT',
6843
+ 'INSURANCE',
6844
+ 'OTHER'
6845
+ ]
6180
6846
  },
6181
- totalPlatforms: {
6182
- type: 'number',
6183
- description: 'Total number of platforms'
6847
+ suggestedSegment: {
6848
+ type: 'string',
6849
+ description:
6850
+ 'Suggested path segment — canonical, already in ACCOUNT_RE format'
6851
+ },
6852
+ logoUrl: {
6853
+ type: 'string',
6854
+ description: 'Logo URL',
6855
+ nullable: true
6856
+ },
6857
+ matchType: {
6858
+ type: 'string',
6859
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
6860
+ enum: ['exact', 'prefix', 'substring']
6184
6861
  }
6185
6862
  },
6186
- required: ['totalAccounts', 'totalPlatforms']
6863
+ required: [
6864
+ 'id',
6865
+ 'name',
6866
+ 'canonical',
6867
+ 'type',
6868
+ 'suggestedSegment',
6869
+ 'logoUrl',
6870
+ 'matchType'
6871
+ ]
6187
6872
  } as const;
6188
6873
 
6189
- export const $AccountsResponseDto = {
6874
+ export const $PlatformMatchResponseDto = {
6190
6875
  type: 'object',
6191
6876
  properties: {
6192
- groups: {
6193
- description: 'Account groups by platform',
6877
+ platforms: {
6878
+ description: 'Ranked matches, best tier first (at most 10 rows)',
6194
6879
  type: 'array',
6195
6880
  items: {
6196
- $ref: '#/components/schemas/PlatformGroupDto'
6881
+ $ref: '#/components/schemas/PlatformMatchResultDto'
6197
6882
  }
6198
6883
  },
6199
- summary: {
6200
- description: 'Summary statistics',
6201
- allOf: [
6202
- {
6203
- $ref: '#/components/schemas/AccountsSummaryDto'
6204
- }
6205
- ]
6884
+ matchType: {
6885
+ type: 'string',
6886
+ description:
6887
+ "Overall match quality — top row's tier, or 'none' when no hits",
6888
+ enum: ['none', 'exact', 'prefix', 'substring']
6889
+ },
6890
+ total: {
6891
+ type: 'number',
6892
+ description: 'Total matches before LIMIT (truncation transparency)'
6893
+ },
6894
+ hasMore: {
6895
+ type: 'boolean',
6896
+ description: 'true when total > platforms.length (more matches exist)'
6206
6897
  }
6207
6898
  },
6208
- required: ['groups', 'summary']
6899
+ required: ['platforms', 'matchType', 'total', 'hasMore']
6209
6900
  } as const;
6210
6901
 
6211
- export const $AccountItemWithAssetClassDto = {
6902
+ export const $CreatePlatformDto = {
6212
6903
  type: 'object',
6213
6904
  properties: {
6214
- id: {
6905
+ name: {
6215
6906
  type: 'string',
6216
- description: 'Account ID'
6907
+ description: 'Platform name',
6908
+ example: 'Binance'
6217
6909
  },
6218
- name: {
6910
+ canonical: {
6219
6911
  type: 'string',
6220
- description: 'Full account name',
6221
- example: 'Assets:Bank:CMB:Savings'
6912
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
6913
+ example: 'binance'
6222
6914
  },
6223
- displayName: {
6915
+ aliases: {
6916
+ description: 'Platform aliases (multi-language names for lookup)',
6917
+ example: ['Binance', 'Binance Exchange', 'BNB'],
6918
+ type: 'array',
6919
+ items: {
6920
+ type: 'string'
6921
+ }
6922
+ },
6923
+ url: {
6224
6924
  type: 'string',
6225
- description: 'Display name (last part of account path)',
6226
- example: 'Savings'
6925
+ description: 'Platform URL',
6926
+ example: 'https://www.binance.com'
6227
6927
  },
6228
- balance: {
6928
+ type: {
6229
6929
  type: 'string',
6230
- description: 'Account balance',
6231
- example: '50000.00'
6930
+ description: 'Platform type',
6931
+ enum: [
6932
+ 'BANK',
6933
+ 'BROKERAGE',
6934
+ 'CRYPTO_EXCHANGE',
6935
+ 'PAYMENT',
6936
+ 'INVESTMENT',
6937
+ 'INSURANCE',
6938
+ 'OTHER'
6939
+ ],
6940
+ example: 'CRYPTO_EXCHANGE'
6232
6941
  },
6233
- currency: {
6942
+ logoUrl: {
6234
6943
  type: 'string',
6235
- description: 'Currency code',
6236
- example: 'CNY'
6944
+ description: 'Platform logo URL',
6945
+ example: 'https://example.com/logos/binance.png'
6237
6946
  },
6238
- assetClass: {
6947
+ isActive: {
6948
+ type: 'boolean',
6949
+ description: 'Whether the platform is active',
6950
+ default: true
6951
+ }
6952
+ },
6953
+ required: ['name', 'canonical', 'aliases', 'url', 'type']
6954
+ } as const;
6955
+
6956
+ export const $UpdatePlatformDto = {
6957
+ type: 'object',
6958
+ properties: {
6959
+ name: {
6239
6960
  type: 'string',
6240
- description: 'Asset class',
6241
- example: 'LIQUIDITY'
6961
+ description: 'Platform name',
6962
+ example: 'Binance'
6242
6963
  },
6243
- assetSubClass: {
6964
+ canonical: {
6244
6965
  type: 'string',
6245
- description: 'Asset sub-class (Prisma-compatible)',
6246
- example: 'RETIREMENT_ACCOUNT'
6966
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
6967
+ example: 'binance'
6247
6968
  },
6248
- regionalSubClass: {
6969
+ aliases: {
6970
+ description: 'Platform aliases (multi-language names for lookup)',
6971
+ example: ['Binance', 'Binance Exchange', 'BNB'],
6972
+ type: 'array',
6973
+ items: {
6974
+ type: 'string'
6975
+ }
6976
+ },
6977
+ url: {
6249
6978
  type: 'string',
6250
- description: 'Regional sub-class (region-specific, for display)',
6251
- example: 'FOUR_ZERO_ONE_K'
6979
+ description: 'Platform URL',
6980
+ example: 'https://www.binance.com'
6252
6981
  },
6253
- riskLevel: {
6982
+ type: {
6254
6983
  type: 'string',
6255
- description: 'Risk level',
6256
- example: 'LOW'
6984
+ description: 'Platform type',
6985
+ enum: [
6986
+ 'BANK',
6987
+ 'BROKERAGE',
6988
+ 'CRYPTO_EXCHANGE',
6989
+ 'PAYMENT',
6990
+ 'INVESTMENT',
6991
+ 'INSURANCE',
6992
+ 'OTHER'
6993
+ ],
6994
+ example: 'CRYPTO_EXCHANGE'
6257
6995
  },
6258
- source: {
6996
+ logoUrl: {
6259
6997
  type: 'string',
6260
- description:
6261
- 'ADR-0105 classification provenance (holding level always; account level only on FALLBACK)',
6262
- enum: ['USER_META', 'FIAT_CURRENCY', 'OPENBB_MAPPING', 'FALLBACK']
6998
+ description: 'Platform logo URL',
6999
+ example: 'https://example.com/logos/binance.png'
7000
+ },
7001
+ isActive: {
7002
+ type: 'boolean',
7003
+ description: 'Whether the platform is active'
6263
7004
  }
6264
- },
6265
- required: ['id', 'name', 'displayName', 'balance', 'currency', 'assetClass']
7005
+ }
6266
7006
  } as const;
6267
7007
 
6268
- export const $AssetClassGroupDto = {
7008
+ export const $NetWorthByCurrencyDto = {
6269
7009
  type: 'object',
6270
7010
  properties: {
6271
- assetClass: {
6272
- type: 'string',
6273
- description: 'Asset class name',
6274
- example: 'LIQUIDITY',
6275
- enum: [
6276
- 'LIQUIDITY',
6277
- 'EQUITY',
6278
- 'FIXED_INCOME',
6279
- 'PRECIOUS_METALS',
6280
- 'COMMODITY',
6281
- 'INSURANCE',
6282
- 'ALTERNATIVE_INVESTMENT',
6283
- 'PERSONAL_ASSETS',
6284
- 'LIABILITY',
6285
- 'REAL_ESTATE',
6286
- 'INDEX'
6287
- ]
6288
- },
6289
- assetSubClass: {
6290
- type: 'string',
6291
- description: 'Asset sub-class name',
6292
- example: 'DEPOSIT'
6293
- },
6294
- accounts: {
6295
- description: 'Accounts within this asset class',
7011
+ netWorth: {
7012
+ description: 'Net worth by currency',
6296
7013
  type: 'array',
6297
7014
  items: {
6298
- $ref: '#/components/schemas/AccountItemWithAssetClassDto'
7015
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6299
7016
  }
6300
7017
  },
6301
- balanceByCurrency: {
6302
- description: 'Balances grouped by currency',
7018
+ assets: {
7019
+ description: 'Assets by currency',
6303
7020
  type: 'array',
6304
7021
  items: {
6305
7022
  $ref: '#/components/schemas/BalanceByCurrencyDto'
6306
7023
  }
6307
7024
  },
6308
- convertedBalance: {
6309
- type: 'string',
6310
- description: 'Converted balance in base currency',
6311
- example: '100000.00'
7025
+ liabilities: {
7026
+ description: 'Liabilities by currency',
7027
+ type: 'array',
7028
+ items: {
7029
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
7030
+ }
6312
7031
  }
6313
7032
  },
6314
- required: ['assetClass', 'accounts', 'balanceByCurrency']
7033
+ required: ['netWorth', 'assets', 'liabilities']
6315
7034
  } as const;
6316
7035
 
6317
- export const $AccountExchangeRateWarningDto = {
7036
+ export const $ConvertedNetWorthDto = {
6318
7037
  type: 'object',
6319
7038
  properties: {
6320
- type: {
7039
+ baseCurrency: {
6321
7040
  type: 'string',
6322
- description: 'Warning type',
6323
- example: 'MISSING_EXCHANGE_RATE'
7041
+ description: 'Base currency for conversion',
7042
+ example: 'CNY'
6324
7043
  },
6325
- currency: {
7044
+ netWorth: {
6326
7045
  type: 'string',
6327
- description: 'Currency without exchange rate',
6328
- example: 'USD'
7046
+ description: 'Converted net worth',
7047
+ example: '500000.00'
6329
7048
  },
6330
- accounts: {
6331
- description: 'Affected account paths',
6332
- type: 'array',
6333
- items: {
6334
- type: 'string'
6335
- }
7049
+ assets: {
7050
+ type: 'string',
7051
+ description: 'Converted assets',
7052
+ example: '600000.00'
6336
7053
  },
6337
- totalAmount: {
7054
+ liabilities: {
6338
7055
  type: 'string',
6339
- description: 'Total amount in this currency',
6340
- example: '5000.00'
7056
+ description: 'Converted liabilities',
7057
+ example: '100000.00'
7058
+ },
7059
+ exchangeRates: {
7060
+ type: 'object',
7061
+ description: 'Exchange rates used for conversion',
7062
+ example: {
7063
+ USD: '7.200000',
7064
+ EUR: '7.800000'
7065
+ }
6341
7066
  }
6342
7067
  },
6343
- required: ['type', 'currency', 'accounts', 'totalAmount']
7068
+ required: [
7069
+ 'baseCurrency',
7070
+ 'netWorth',
7071
+ 'assets',
7072
+ 'liabilities',
7073
+ 'exchangeRates'
7074
+ ]
6344
7075
  } as const;
6345
7076
 
6346
- export const $AssetClassSummaryDto = {
7077
+ export const $NetWorthResponseDto = {
6347
7078
  type: 'object',
6348
7079
  properties: {
6349
- totalAccounts: {
6350
- type: 'number',
6351
- description: 'Total number of accounts'
7080
+ netWorth: {
7081
+ type: 'string',
7082
+ description:
7083
+ 'Total net worth (assets - liabilities, converted to base currency)',
7084
+ example: '500000.00'
6352
7085
  },
6353
- totalAssetClasses: {
6354
- type: 'number',
6355
- description: 'Total number of asset classes'
7086
+ assets: {
7087
+ type: 'string',
7088
+ description: 'Total assets value (converted)',
7089
+ example: '600000.00'
6356
7090
  },
6357
- baseCurrency: {
7091
+ liabilities: {
6358
7092
  type: 'string',
6359
- description: 'Base currency for conversion',
6360
- example: 'CNY'
7093
+ description: 'Total liabilities value (positive number, converted)',
7094
+ example: '100000.00'
6361
7095
  },
6362
- warnings: {
6363
- description: 'Exchange rate warnings',
6364
- type: 'array',
6365
- items: {
6366
- $ref: '#/components/schemas/AccountExchangeRateWarningDto'
6367
- }
7096
+ monthlyReturn: {
7097
+ type: 'string',
7098
+ description: 'Monthly return (change from last month)',
7099
+ example: '15000.00'
6368
7100
  },
6369
- fallback: {
6370
- type: 'object',
6371
- description:
6372
- 'ADR-0105 §4 fallback provenance stats (holding level only). valueRatio is the grey-area share of total converted value; count is the number of source=FALLBACK holdings.'
6373
- }
6374
- },
6375
- required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
6376
- } as const;
6377
-
6378
- export const $AssetClassAccountsResponseDto = {
6379
- type: 'object',
6380
- properties: {
6381
- groups: {
6382
- description: 'Account groups by asset class',
6383
- type: 'array',
6384
- items: {
6385
- $ref: '#/components/schemas/AssetClassGroupDto'
6386
- }
7101
+ monthlyReturnPercentage: {
7102
+ type: 'string',
7103
+ description: 'Monthly return percentage',
7104
+ example: '3.15'
6387
7105
  },
6388
- summary: {
6389
- description: 'Summary statistics',
7106
+ currency: {
7107
+ type: 'string',
7108
+ description: 'Base currency code',
7109
+ example: 'CNY'
7110
+ },
7111
+ asOf: {
7112
+ type: 'string',
7113
+ description: 'Data as of date (ISO 8601)',
7114
+ example: '2024-06-15T00:00:00.000Z'
7115
+ },
7116
+ byCurrency: {
7117
+ description: 'Balances grouped by original currency',
6390
7118
  allOf: [
6391
7119
  {
6392
- $ref: '#/components/schemas/AssetClassSummaryDto'
7120
+ $ref: '#/components/schemas/NetWorthByCurrencyDto'
6393
7121
  }
6394
7122
  ]
6395
7123
  },
6396
- uncategorized: {
7124
+ converted: {
6397
7125
  description:
6398
- 'ADR-0105 §6 holding-level grey-area bucket (source=FALLBACK holdings peeled out of groups). Present only for groupBy=holdingAssetClass when FALLBACK holdings exist.',
7126
+ 'Converted values in base currency (undefined if no exchange rates available)',
6399
7127
  allOf: [
6400
7128
  {
6401
- $ref: '#/components/schemas/AssetClassGroupDto'
7129
+ $ref: '#/components/schemas/ConvertedNetWorthDto'
6402
7130
  }
6403
7131
  ]
7132
+ },
7133
+ warnings: {
7134
+ description: 'Exchange rate warnings',
7135
+ type: 'array',
7136
+ items: {
7137
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
7138
+ }
6404
7139
  }
6405
7140
  },
6406
- required: ['groups', 'summary']
7141
+ required: [
7142
+ 'netWorth',
7143
+ 'assets',
7144
+ 'liabilities',
7145
+ 'monthlyReturn',
7146
+ 'monthlyReturnPercentage',
7147
+ 'currency',
7148
+ 'asOf'
7149
+ ]
6407
7150
  } as const;
6408
7151
 
6409
- export const $HoldingAssetClassAccountSliceDto = {
7152
+ export const $AccountItemDto = {
6410
7153
  type: 'object',
6411
7154
  properties: {
6412
- accountId: {
7155
+ id: {
6413
7156
  type: 'string',
6414
7157
  description: 'Account ID'
6415
7158
  },
6416
- accountPath: {
7159
+ name: {
6417
7160
  type: 'string',
6418
- description: 'Full account path',
6419
- example: 'Assets:US:Investments:Brokerage'
7161
+ description: 'Full account name',
7162
+ example: 'Assets:CN:CMB:Savings'
6420
7163
  },
6421
- accountCurrency: {
7164
+ displayName: {
6422
7165
  type: 'string',
6423
- description:
6424
- 'Currency of the holding with the largest converted base value; undefined when no holding is convertible',
6425
- example: 'USD'
7166
+ description: 'Display name (last part of account path)',
7167
+ example: 'Savings'
6426
7168
  },
6427
- marketValueBase: {
7169
+ balance: {
6428
7170
  type: 'string',
6429
- description:
6430
- "Account's market value in base currency (Σ converted holdings; grey bucket included)",
7171
+ description: 'Account balance',
6431
7172
  example: '50000.00'
6432
7173
  },
6433
- shareOfTotalPct: {
6434
- type: 'number',
7174
+ currency: {
7175
+ type: 'string',
7176
+ description: 'Currency code',
7177
+ example: 'CNY'
7178
+ },
7179
+ convertedBalance: {
7180
+ type: 'string',
6435
7181
  description:
6436
- 'Share of the global total (0-100). 0 when globalTotal is zero (no NaN/Infinity).',
6437
- example: 42.5
7182
+ 'FX-converted balance in base currency; omitted when not convertible',
7183
+ example: '50000.00'
7184
+ }
7185
+ },
7186
+ required: ['id', 'name', 'displayName', 'balance', 'currency']
7187
+ } as const;
7188
+
7189
+ export const $PlatformGroupDto = {
7190
+ type: 'object',
7191
+ properties: {
7192
+ platformId: {
7193
+ type: 'string',
7194
+ description: 'Platform ID'
6438
7195
  },
6439
- groups: {
6440
- description: 'Per-account asset-class breakdown',
7196
+ platformName: {
7197
+ type: 'string',
7198
+ description: 'Platform display name',
7199
+ example: 'CMB Bank'
7200
+ },
7201
+ accounts: {
7202
+ description: 'Accounts within this platform',
6441
7203
  type: 'array',
6442
7204
  items: {
6443
- $ref: '#/components/schemas/AssetClassGroupDto'
7205
+ $ref: '#/components/schemas/AccountItemDto'
7206
+ }
7207
+ },
7208
+ totalBalance: {
7209
+ type: 'string',
7210
+ description: 'FX-converted total balance in base currency',
7211
+ example: '100000.00'
7212
+ },
7213
+ balanceByCurrency: {
7214
+ description: 'Raw (unconverted) balances grouped by currency',
7215
+ type: 'array',
7216
+ items: {
7217
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6444
7218
  }
6445
7219
  },
6446
- uncategorized: {
7220
+ convertedBalance: {
7221
+ type: 'string',
6447
7222
  description:
6448
- 'Per-account grey bucket (source=FALLBACK holdings, incl. broker cash)',
6449
- allOf: [
6450
- {
6451
- $ref: '#/components/schemas/AssetClassGroupDto'
6452
- }
6453
- ]
7223
+ 'Converted balance in base currency (omitted when no currency is convertible)',
7224
+ example: '100000.00'
6454
7225
  },
6455
- holdings: {
7226
+ sharePct: {
7227
+ type: 'number',
6456
7228
  description:
6457
- 'Every holding row for this account (account ID in each row’s `id` field)',
6458
- type: 'array',
6459
- items: {
6460
- $ref: '#/components/schemas/AccountItemWithAssetClassDto'
6461
- }
7229
+ 'Share of the grand converted total (0-100); 0 when grand total is 0',
7230
+ example: 42.5
6462
7231
  }
6463
7232
  },
6464
7233
  required: [
6465
- 'accountId',
6466
- 'accountPath',
6467
- 'marketValueBase',
6468
- 'shareOfTotalPct',
6469
- 'groups',
6470
- 'holdings'
7234
+ 'platformId',
7235
+ 'platformName',
7236
+ 'accounts',
7237
+ 'totalBalance',
7238
+ 'balanceByCurrency',
7239
+ 'sharePct'
6471
7240
  ]
6472
7241
  } as const;
6473
7242
 
6474
- export const $HoldingAssetClassCrossAccountResponseDto = {
7243
+ export const $AccountExchangeRateWarningDto = {
6475
7244
  type: 'object',
6476
7245
  properties: {
6477
- global: {
6478
- description: 'Merged cross-account holding aggregation',
6479
- allOf: [
6480
- {
6481
- $ref: '#/components/schemas/AssetClassAccountsResponseDto'
6482
- }
6483
- ]
7246
+ type: {
7247
+ type: 'string',
7248
+ description: 'Warning type',
7249
+ example: 'MISSING_EXCHANGE_RATE'
6484
7250
  },
6485
- byAccount: {
6486
- description: 'Per-account slices',
7251
+ currency: {
7252
+ type: 'string',
7253
+ description: 'Currency without exchange rate',
7254
+ example: 'USD'
7255
+ },
7256
+ accounts: {
7257
+ description: 'Affected account paths',
6487
7258
  type: 'array',
6488
7259
  items: {
6489
- $ref: '#/components/schemas/HoldingAssetClassAccountSliceDto'
7260
+ type: 'string'
6490
7261
  }
7262
+ },
7263
+ totalAmount: {
7264
+ type: 'string',
7265
+ description: 'Total amount in this currency',
7266
+ example: '5000.00'
6491
7267
  }
6492
7268
  },
6493
- required: ['global', 'byAccount']
7269
+ required: ['type', 'currency', 'accounts', 'totalAmount']
6494
7270
  } as const;
6495
7271
 
6496
- export const $CashFlowByCurrencyDto = {
7272
+ export const $AccountsSummaryDto = {
6497
7273
  type: 'object',
6498
7274
  properties: {
6499
- income: {
6500
- description: 'Income by currency',
6501
- type: 'array',
6502
- items: {
6503
- $ref: '#/components/schemas/BalanceByCurrencyDto'
6504
- }
7275
+ totalAccounts: {
7276
+ type: 'number',
7277
+ description: 'Total number of accounts'
6505
7278
  },
6506
- expense: {
6507
- description: 'Expense by currency',
7279
+ totalPlatforms: {
7280
+ type: 'number',
7281
+ description: 'Total number of platforms'
7282
+ },
7283
+ baseCurrency: {
7284
+ type: 'string',
7285
+ description: 'Base currency for conversion',
7286
+ example: 'CNY'
7287
+ },
7288
+ warnings: {
7289
+ description: 'Per-account exchange rate warnings',
6508
7290
  type: 'array',
6509
7291
  items: {
6510
- $ref: '#/components/schemas/BalanceByCurrencyDto'
7292
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
6511
7293
  }
6512
- },
6513
- netSavings: {
6514
- description: 'Net savings by currency',
7294
+ }
7295
+ },
7296
+ required: ['totalAccounts', 'totalPlatforms', 'baseCurrency']
7297
+ } as const;
7298
+
7299
+ export const $AccountsResponseDto = {
7300
+ type: 'object',
7301
+ properties: {
7302
+ groups: {
7303
+ description: 'Account groups by platform',
6515
7304
  type: 'array',
6516
7305
  items: {
6517
- $ref: '#/components/schemas/BalanceByCurrencyDto'
7306
+ $ref: '#/components/schemas/PlatformGroupDto'
6518
7307
  }
7308
+ },
7309
+ summary: {
7310
+ description: 'Summary statistics',
7311
+ allOf: [
7312
+ {
7313
+ $ref: '#/components/schemas/AccountsSummaryDto'
7314
+ }
7315
+ ]
6519
7316
  }
6520
7317
  },
6521
- required: ['income', 'expense', 'netSavings']
7318
+ required: ['groups', 'summary']
6522
7319
  } as const;
6523
7320
 
6524
- export const $ConvertedCashFlowDto = {
7321
+ export const $AccountItemWithAssetClassDto = {
6525
7322
  type: 'object',
6526
7323
  properties: {
6527
- baseCurrency: {
7324
+ id: {
6528
7325
  type: 'string',
6529
- description: 'Base currency for conversion',
6530
- example: 'CNY'
7326
+ description: 'Account ID'
6531
7327
  },
6532
- income: {
7328
+ name: {
6533
7329
  type: 'string',
6534
- description: 'Converted income',
6535
- example: '25000.00'
7330
+ description: 'Full account name',
7331
+ example: 'Assets:CN:CMB:Savings'
6536
7332
  },
6537
- expense: {
7333
+ displayName: {
6538
7334
  type: 'string',
6539
- description: 'Converted expense',
6540
- example: '18000.00'
7335
+ description: 'Display name (last part of account path)',
7336
+ example: 'Savings'
6541
7337
  },
6542
- netSavings: {
7338
+ balance: {
6543
7339
  type: 'string',
6544
- description: 'Converted net savings',
6545
- example: '7000.00'
7340
+ description: 'Account balance',
7341
+ example: '50000.00'
6546
7342
  },
6547
- exchangeRates: {
6548
- type: 'object',
6549
- description: 'Exchange rates used for conversion',
6550
- example: {
6551
- USD: '7.200000'
6552
- }
6553
- }
6554
- },
6555
- required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
6556
- } as const;
6557
-
6558
- export const $CashFlowResponseDto = {
6559
- type: 'object',
6560
- properties: {
6561
- period: {
7343
+ currency: {
6562
7344
  type: 'string',
6563
- description: 'Period identifier (YYYY-MM)',
6564
- example: '2024-06'
7345
+ description: 'Currency code',
7346
+ example: 'CNY'
6565
7347
  },
6566
- income: {
7348
+ convertedBalance: {
6567
7349
  type: 'string',
6568
- description: 'Total income for the period (converted)',
6569
- example: '25000.00'
7350
+ description:
7351
+ 'FX-converted balance in base currency; omitted when not convertible',
7352
+ example: '50000.00'
6570
7353
  },
6571
- expense: {
7354
+ assetClass: {
6572
7355
  type: 'string',
6573
- description: 'Total expenses for the period (converted)',
6574
- example: '18000.00'
7356
+ description: 'Asset class',
7357
+ example: 'LIQUIDITY'
6575
7358
  },
6576
- netSavings: {
7359
+ assetSubClass: {
6577
7360
  type: 'string',
6578
- description: 'Net savings (income - expense, converted)',
6579
- example: '7000.00'
7361
+ description: 'Asset sub-class (Prisma-compatible)',
7362
+ example: 'RETIREMENT_ACCOUNT'
6580
7363
  },
6581
- savingsRate: {
7364
+ regionalSubClass: {
6582
7365
  type: 'string',
6583
- description: 'Savings rate percentage (netSavings / income * 100)',
6584
- example: '28.00'
7366
+ description: 'Regional sub-class (region-specific, for display)',
7367
+ example: 'FOUR_ZERO_ONE_K'
6585
7368
  },
6586
- currency: {
7369
+ riskLevel: {
6587
7370
  type: 'string',
6588
- description: 'Base currency code',
6589
- example: 'CNY'
6590
- },
6591
- byCurrency: {
6592
- description: 'Cash flow grouped by original currency',
6593
- allOf: [
6594
- {
6595
- $ref: '#/components/schemas/CashFlowByCurrencyDto'
6596
- }
6597
- ]
6598
- },
6599
- converted: {
6600
- description: 'Converted values in base currency',
6601
- allOf: [
6602
- {
6603
- $ref: '#/components/schemas/ConvertedCashFlowDto'
6604
- }
6605
- ]
7371
+ description: 'Risk level',
7372
+ example: 'LOW'
6606
7373
  },
6607
- warnings: {
6608
- description: 'Exchange rate warnings',
6609
- type: 'array',
6610
- items: {
6611
- $ref: '#/components/schemas/ExchangeRateWarningDto'
6612
- }
7374
+ source: {
7375
+ type: 'string',
7376
+ description:
7377
+ 'ADR-0105 classification provenance (holding level always; account level only on FALLBACK)',
7378
+ enum: ['USER_META', 'FIAT_CURRENCY', 'OPENBB_MAPPING', 'FALLBACK']
6613
7379
  }
6614
7380
  },
6615
- required: [
6616
- 'period',
6617
- 'income',
6618
- 'expense',
6619
- 'netSavings',
6620
- 'savingsRate',
6621
- 'currency'
6622
- ]
7381
+ required: ['id', 'name', 'displayName', 'balance', 'currency', 'assetClass']
6623
7382
  } as const;
6624
7383
 
6625
- export const $MonetaryDto = {
7384
+ export const $AssetClassGroupDto = {
6626
7385
  type: 'object',
6627
7386
  properties: {
6628
- amount: {
7387
+ assetClass: {
6629
7388
  type: 'string',
6630
- description: 'Amount (Decimal string)',
6631
- example: '3000'
7389
+ description: 'Asset class name',
7390
+ example: 'LIQUIDITY',
7391
+ enum: [
7392
+ 'LIQUIDITY',
7393
+ 'EQUITY',
7394
+ 'FIXED_INCOME',
7395
+ 'PRECIOUS_METALS',
7396
+ 'COMMODITY',
7397
+ 'INSURANCE',
7398
+ 'ALTERNATIVE_INVESTMENT',
7399
+ 'PERSONAL_ASSETS',
7400
+ 'LIABILITY',
7401
+ 'REAL_ESTATE',
7402
+ 'INDEX'
7403
+ ]
6632
7404
  },
6633
- currency: {
7405
+ assetSubClass: {
6634
7406
  type: 'string',
6635
- description: 'ISO 4217 currency',
6636
- example: 'USD'
7407
+ description: 'Asset sub-class name',
7408
+ example: 'DEPOSIT'
6637
7409
  },
6638
- baseCcyEquivalent: {
6639
- type: 'object',
6640
- description: 'Converted to user base currency (Decimal string)',
6641
- example: '21600',
6642
- nullable: true
7410
+ accounts: {
7411
+ description: 'Accounts within this asset class',
7412
+ type: 'array',
7413
+ items: {
7414
+ $ref: '#/components/schemas/AccountItemWithAssetClassDto'
7415
+ }
7416
+ },
7417
+ balanceByCurrency: {
7418
+ description: 'Balances grouped by currency',
7419
+ type: 'array',
7420
+ items: {
7421
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
7422
+ }
7423
+ },
7424
+ convertedBalance: {
7425
+ type: 'string',
7426
+ description: 'Converted balance in base currency',
7427
+ example: '100000.00'
6643
7428
  }
6644
7429
  },
6645
- required: ['amount', 'currency']
7430
+ required: ['assetClass', 'accounts', 'balanceByCurrency']
6646
7431
  } as const;
6647
7432
 
6648
- export const $CurrentPriceDto = {
7433
+ export const $AssetClassSummaryDto = {
6649
7434
  type: 'object',
6650
7435
  properties: {
6651
- amount: {
6652
- type: 'string',
6653
- description: 'Price amount (Decimal string)',
6654
- example: '250'
7436
+ totalAccounts: {
7437
+ type: 'number',
7438
+ description: 'Total number of accounts'
6655
7439
  },
6656
- currency: {
6657
- type: 'string',
6658
- description: 'Price currency (ISO 4217)',
6659
- example: 'USD'
7440
+ totalAssetClasses: {
7441
+ type: 'number',
7442
+ description: 'Total number of asset classes'
6660
7443
  },
6661
- date: {
7444
+ baseCurrency: {
6662
7445
  type: 'string',
6663
- description: 'Price date (ISO 8601)',
6664
- example: '2024-06-01'
7446
+ description: 'Base currency for conversion',
7447
+ example: 'CNY'
6665
7448
  },
6666
- source: {
6667
- type: 'string',
6668
- description: 'Price source',
6669
- example: 'USER_OVERRIDE',
6670
- enum: ['USER_OVERRIDE', 'OPENBB_EQUITY', 'OPENBB_CURRENCY']
7449
+ warnings: {
7450
+ description: 'Exchange rate warnings',
7451
+ type: 'array',
7452
+ items: {
7453
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
7454
+ }
7455
+ },
7456
+ fallback: {
7457
+ type: 'object',
7458
+ description:
7459
+ 'ADR-0105 §4 fallback provenance stats (holding level only). valueRatio is the grey-area share of total converted value; count is the number of source=FALLBACK holdings.'
6671
7460
  }
6672
7461
  },
6673
- required: ['amount', 'currency', 'date', 'source']
7462
+ required: ['totalAccounts', 'totalAssetClasses', 'baseCurrency']
6674
7463
  } as const;
6675
7464
 
6676
- export const $FxRateDto = {
7465
+ export const $AssetClassAccountsResponseDto = {
6677
7466
  type: 'object',
6678
7467
  properties: {
6679
- from: {
6680
- type: 'string',
6681
- example: 'USD'
6682
- },
6683
- to: {
6684
- type: 'string',
6685
- example: 'CNY'
7468
+ groups: {
7469
+ description: 'Account groups by asset class',
7470
+ type: 'array',
7471
+ items: {
7472
+ $ref: '#/components/schemas/AssetClassGroupDto'
7473
+ }
6686
7474
  },
6687
- rate: {
6688
- type: 'string',
6689
- description: 'FX rate (Decimal string)',
6690
- example: '7.2'
7475
+ summary: {
7476
+ description: 'Summary statistics',
7477
+ allOf: [
7478
+ {
7479
+ $ref: '#/components/schemas/AssetClassSummaryDto'
7480
+ }
7481
+ ]
6691
7482
  },
6692
- date: {
6693
- type: 'string',
6694
- description: 'Rate date (ISO 8601)',
6695
- example: '2024-01-15'
7483
+ uncategorized: {
7484
+ description:
7485
+ 'ADR-0105 §6 holding-level grey-area bucket (source=FALLBACK holdings peeled out of groups). Present only for groupBy=holdingAssetClass when FALLBACK holdings exist.',
7486
+ allOf: [
7487
+ {
7488
+ $ref: '#/components/schemas/AssetClassGroupDto'
7489
+ }
7490
+ ]
6696
7491
  }
6697
7492
  },
6698
- required: ['from', 'to', 'rate', 'date']
7493
+ required: ['groups', 'summary']
6699
7494
  } as const;
6700
7495
 
6701
- export const $HoldingPnlRowDto = {
7496
+ export const $HoldingAssetClassAccountSliceDto = {
6702
7497
  type: 'object',
6703
7498
  properties: {
6704
7499
  accountId: {
6705
7500
  type: 'string',
6706
- description: 'Account UUID',
6707
- example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
7501
+ description: 'Account ID'
6708
7502
  },
6709
7503
  accountPath: {
6710
7504
  type: 'string',
6711
7505
  description: 'Full account path',
6712
- example: 'Assets:US:Broker:AAPL'
7506
+ example: 'Assets:US:Fidelity:Brokerage'
6713
7507
  },
6714
- accountCcy: {
6715
- type: 'object',
6716
- description: 'Account settlement currency (ISO 4217), from cost currency',
6717
- nullable: true,
6718
- example: 'USD'
6719
- },
6720
- brokerType: {
6721
- type: 'object',
6722
- description: 'Broker type derived from Platform.type',
6723
- nullable: true,
6724
- example: 'broker'
6725
- },
6726
- symbol: {
6727
- type: 'string',
6728
- description: 'Commodity symbol',
6729
- example: 'AAPL'
6730
- },
6731
- chartToken: {
6732
- type: 'string',
6733
- description: 'Chart segment token (libs/common resolver)',
6734
- example: 'equity',
6735
- enum: ['equity', 'fund', 'bond', 'cash', 'other']
6736
- },
6737
- assetClass: {
7508
+ accountCurrency: {
6738
7509
  type: 'string',
6739
- example: 'EQUITY'
6740
- },
6741
- assetSubClass: {
6742
- type: 'object',
6743
- nullable: true,
6744
- example: 'STOCK'
7510
+ description:
7511
+ 'Currency of the holding with the largest converted base value; undefined when no holding is convertible',
7512
+ example: 'USD'
6745
7513
  },
6746
- units: {
7514
+ marketValueBase: {
6747
7515
  type: 'string',
6748
- description: 'Net held units (Decimal string)',
6749
- example: '12'
6750
- },
6751
- averageCostPerUnit: {
6752
7516
  description:
6753
- 'Average cost per unit; null when cost currency conflicts or no cost',
6754
- nullable: true,
6755
- allOf: [
6756
- {
6757
- $ref: '#/components/schemas/MonetaryDto'
6758
- }
6759
- ]
6760
- },
6761
- costBasis: {
6762
- description: 'Cost basis of held units',
6763
- nullable: true,
6764
- allOf: [
6765
- {
6766
- $ref: '#/components/schemas/MonetaryDto'
6767
- }
6768
- ]
6769
- },
6770
- marketValue: {
6771
- description: 'Market value at asOf price',
6772
- nullable: true,
6773
- allOf: [
6774
- {
6775
- $ref: '#/components/schemas/MonetaryDto'
6776
- }
6777
- ]
6778
- },
6779
- currentPrice: {
6780
- description: 'Price used for market value',
6781
- nullable: true,
6782
- allOf: [
6783
- {
6784
- $ref: '#/components/schemas/CurrentPriceDto'
6785
- }
6786
- ]
7517
+ "Account's market value in base currency (Σ converted holdings; grey bucket included)",
7518
+ example: '50000.00'
6787
7519
  },
6788
- unrealizedPnlBase: {
6789
- type: 'object',
7520
+ shareOfTotalPct: {
7521
+ type: 'number',
6790
7522
  description:
6791
- 'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
6792
- nullable: true,
6793
- example: '6000'
6794
- },
6795
- unrealizedPnlPct: {
6796
- type: 'object',
6797
- description: 'Unrealized P&L % (Decimal string)',
6798
- nullable: true,
6799
- example: '25'
7523
+ 'Share of the global total (0-100). 0 when globalTotal is zero (no NaN/Infinity).',
7524
+ example: 42.5
6800
7525
  },
6801
- costFxRate: {
6802
- description: 'Historical FX rate applied to cost basis',
6803
- nullable: true,
6804
- allOf: [
6805
- {
6806
- $ref: '#/components/schemas/FxRateDto'
6807
- }
6808
- ]
7526
+ groups: {
7527
+ description: 'Per-account asset-class breakdown',
7528
+ type: 'array',
7529
+ items: {
7530
+ $ref: '#/components/schemas/AssetClassGroupDto'
7531
+ }
6809
7532
  },
6810
- marketFxRate: {
6811
- description: 'FX rate applied to market value',
6812
- nullable: true,
7533
+ uncategorized: {
7534
+ description:
7535
+ 'Per-account grey bucket (source=FALLBACK holdings, incl. broker cash)',
6813
7536
  allOf: [
6814
7537
  {
6815
- $ref: '#/components/schemas/FxRateDto'
7538
+ $ref: '#/components/schemas/AssetClassGroupDto'
6816
7539
  }
6817
7540
  ]
6818
7541
  },
6819
- pctOfInvestedAssets: {
6820
- type: 'object',
6821
- description:
6822
- 'Share of invested assets % (Decimal string); only for invested chartTokens',
6823
- nullable: true,
6824
- example: '40'
6825
- },
6826
- realizedPnl: {
7542
+ holdings: {
6827
7543
  description:
6828
- 'Cumulative realized P&L on sold lots (asOf-date cutoff); null when the method has no applicable sells, a sell lacks a price, or any required FX rate is missing (never-mix). When a sell spans multiple currencies (cross-currency sale), amount and currency reflect the base currency; baseCcyEquivalent is always the authoritative dual-FX figure',
6829
- nullable: true,
6830
- allOf: [
6831
- {
6832
- $ref: '#/components/schemas/MonetaryDto'
6833
- }
6834
- ]
7544
+ 'Every holding row for this account (account ID in each row’s `id` field)',
7545
+ type: 'array',
7546
+ items: {
7547
+ $ref: '#/components/schemas/AccountItemWithAssetClassDto'
7548
+ }
6835
7549
  }
6836
7550
  },
6837
7551
  required: [
6838
7552
  'accountId',
6839
7553
  'accountPath',
6840
- 'symbol',
6841
- 'chartToken',
6842
- 'assetClass',
6843
- 'units'
7554
+ 'marketValueBase',
7555
+ 'shareOfTotalPct',
7556
+ 'groups',
7557
+ 'holdings'
6844
7558
  ]
6845
7559
  } as const;
6846
7560
 
6847
- export const $HoldingPnlWarningDto = {
7561
+ export const $HoldingAssetClassCrossAccountResponseDto = {
6848
7562
  type: 'object',
6849
7563
  properties: {
6850
- type: {
6851
- type: 'string',
6852
- description: 'Warning type',
6853
- example: 'MISSING_COST_FX_RATE',
6854
- enum: [
6855
- 'MISSING_COST_FX_RATE',
6856
- 'MISSING_MARKET_FX_RATE',
6857
- 'MISSING_SALE_PRICE',
6858
- 'MISSING_REALIZED_FX_RATE',
6859
- 'OVERSOLD_LOTS',
6860
- 'NO_PRICE',
6861
- 'MIXED_COST_CURRENCY'
7564
+ global: {
7565
+ description: 'Merged cross-account holding aggregation',
7566
+ allOf: [
7567
+ {
7568
+ $ref: '#/components/schemas/AssetClassAccountsResponseDto'
7569
+ }
6862
7570
  ]
6863
7571
  },
6864
- symbol: {
6865
- type: 'object',
6866
- nullable: true
6867
- },
6868
- accountId: {
6869
- type: 'object',
6870
- nullable: true
6871
- },
6872
- currency: {
6873
- type: 'object',
6874
- nullable: true
7572
+ byAccount: {
7573
+ description: 'Per-account slices',
7574
+ type: 'array',
7575
+ items: {
7576
+ $ref: '#/components/schemas/HoldingAssetClassAccountSliceDto'
7577
+ }
6875
7578
  }
6876
7579
  },
6877
- required: ['type']
7580
+ required: ['global', 'byAccount']
6878
7581
  } as const;
6879
7582
 
6880
- export const $HoldingPnlResponseDto = {
7583
+ export const $CashFlowByCurrencyDto = {
6881
7584
  type: 'object',
6882
7585
  properties: {
6883
- asOfDate: {
6884
- type: 'string',
6885
- example: '2026-07-08'
6886
- },
6887
- baseCurrency: {
6888
- type: 'string',
6889
- example: 'CNY'
6890
- },
6891
- method: {
6892
- type: 'string',
6893
- description:
6894
- 'Realized-P&L lot-matching method (FIFO or average). Unrealized cost basis remains average regardless of this value (#473).',
6895
- enum: ['average', 'FIFO'],
6896
- example: 'average'
7586
+ income: {
7587
+ description: 'Income by currency',
7588
+ type: 'array',
7589
+ items: {
7590
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
7591
+ }
6897
7592
  },
6898
- rows: {
7593
+ expense: {
7594
+ description: 'Expense by currency',
6899
7595
  type: 'array',
6900
7596
  items: {
6901
- $ref: '#/components/schemas/HoldingPnlRowDto'
7597
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6902
7598
  }
6903
7599
  },
6904
- warnings: {
7600
+ netSavings: {
7601
+ description: 'Net savings by currency',
6905
7602
  type: 'array',
6906
7603
  items: {
6907
- $ref: '#/components/schemas/HoldingPnlWarningDto'
7604
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6908
7605
  }
6909
7606
  }
6910
7607
  },
6911
- required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
7608
+ required: ['income', 'expense', 'netSavings']
6912
7609
  } as const;
6913
7610
 
6914
- export const $CreateBeanPriceDto = {
7611
+ export const $ConvertedCashFlowDto = {
6915
7612
  type: 'object',
6916
7613
  properties: {
6917
- currency: {
7614
+ baseCurrency: {
6918
7615
  type: 'string',
6919
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6920
- example: 'USD'
7616
+ description: 'Base currency for conversion',
7617
+ example: 'CNY'
6921
7618
  },
6922
- quoteCurrency: {
7619
+ income: {
6923
7620
  type: 'string',
6924
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
6925
- example: 'CNY'
7621
+ description: 'Converted income',
7622
+ example: '25000.00'
6926
7623
  },
6927
- amount: {
6928
- type: 'number',
6929
- description:
6930
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
6931
- example: 175.5,
6932
- minimum: 0
7624
+ expense: {
7625
+ type: 'string',
7626
+ description: 'Converted expense',
7627
+ example: '18000.00'
6933
7628
  },
6934
- date: {
7629
+ netSavings: {
6935
7630
  type: 'string',
6936
- description: 'Price date (ISO 8601 format)',
6937
- example: '2024-11-05'
7631
+ description: 'Converted net savings',
7632
+ example: '7000.00'
6938
7633
  },
6939
- metadata: {
7634
+ exchangeRates: {
6940
7635
  type: 'object',
6941
- description:
6942
- 'Metadata (validated by Zod schema, max field lengths enforced)',
7636
+ description: 'Exchange rates used for conversion',
6943
7637
  example: {
6944
- source: 'MANUAL',
6945
- note: 'Bank valuation report',
6946
- confidence: 0.95
7638
+ USD: '7.200000'
6947
7639
  }
6948
7640
  }
6949
7641
  },
6950
- required: ['currency', 'quoteCurrency', 'amount', 'date']
7642
+ required: ['baseCurrency', 'income', 'expense', 'netSavings', 'exchangeRates']
6951
7643
  } as const;
6952
7644
 
6953
- export const $PriceResponseDto = {
7645
+ export const $CashFlowResponseDto = {
6954
7646
  type: 'object',
6955
7647
  properties: {
6956
- id: {
7648
+ period: {
6957
7649
  type: 'string',
6958
- description: 'Unique identifier',
6959
- example: 'uuid-123-456'
7650
+ description: 'Period identifier (YYYY-MM)',
7651
+ example: '2024-06'
6960
7652
  },
6961
- userId: {
7653
+ income: {
6962
7654
  type: 'string',
6963
- description: 'User ID (owner of the price)',
6964
- example: 'user-123'
7655
+ description: 'Total income for the period (converted)',
7656
+ example: '25000.00'
6965
7657
  },
6966
- currency: {
7658
+ expense: {
6967
7659
  type: 'string',
6968
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6969
- example: 'BTC'
7660
+ description: 'Total expenses for the period (converted)',
7661
+ example: '18000.00'
6970
7662
  },
6971
- quoteCurrency: {
7663
+ netSavings: {
6972
7664
  type: 'string',
6973
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
6974
- example: 'USD'
7665
+ description: 'Net savings (income - expense, converted)',
7666
+ example: '7000.00'
6975
7667
  },
6976
- amount: {
6977
- type: 'number',
6978
- description:
6979
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
6980
- example: 50000
7668
+ savingsRate: {
7669
+ type: 'string',
7670
+ description: 'Savings rate percentage (netSavings / income * 100)',
7671
+ example: '28.00'
6981
7672
  },
6982
- date: {
7673
+ currency: {
7674
+ type: 'string',
7675
+ description: 'Base currency code',
7676
+ example: 'CNY'
7677
+ },
7678
+ byCurrency: {
7679
+ description: 'Cash flow grouped by original currency',
7680
+ allOf: [
7681
+ {
7682
+ $ref: '#/components/schemas/CashFlowByCurrencyDto'
7683
+ }
7684
+ ]
7685
+ },
7686
+ converted: {
7687
+ description: 'Converted values in base currency',
7688
+ allOf: [
7689
+ {
7690
+ $ref: '#/components/schemas/ConvertedCashFlowDto'
7691
+ }
7692
+ ]
7693
+ },
7694
+ warnings: {
7695
+ description: 'Exchange rate warnings',
7696
+ type: 'array',
7697
+ items: {
7698
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
7699
+ }
7700
+ }
7701
+ },
7702
+ required: [
7703
+ 'period',
7704
+ 'income',
7705
+ 'expense',
7706
+ 'netSavings',
7707
+ 'savingsRate',
7708
+ 'currency'
7709
+ ]
7710
+ } as const;
7711
+
7712
+ export const $CategoryGroupDto = {
7713
+ type: 'object',
7714
+ properties: {
7715
+ category: {
6983
7716
  type: 'string',
6984
7717
  description:
6985
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
6986
- example: '2024-01-01',
6987
- format: 'date'
7718
+ 'Functional category (account-path Group segment); regional and universal account paths merge under it',
7719
+ example: 'Food'
6988
7720
  },
6989
- meta: {
6990
- type: 'object',
7721
+ totalExpense: {
7722
+ type: 'string',
6991
7723
  description:
6992
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
6993
- example: {
6994
- source: 'MANUAL',
6995
- note: 'User-defined price',
6996
- confidence: 1
7724
+ 'Converted total for this category in base currency (expense amount when flow=expense, income amount when flow=income)',
7725
+ example: '1200.00'
7726
+ },
7727
+ sharePct: {
7728
+ type: 'number',
7729
+ description: 'Share of grand total (0-100); 0 when grand total is 0',
7730
+ example: 42.5
7731
+ },
7732
+ balanceByCurrency: {
7733
+ description: 'Raw (unconverted) expense per currency',
7734
+ type: 'array',
7735
+ items: {
7736
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
6997
7737
  }
6998
7738
  },
6999
- createdAt: {
7000
- format: 'date-time',
7739
+ convertedBalance: {
7001
7740
  type: 'string',
7002
- description: 'Creation timestamp',
7003
- example: '2024-11-03T10:00:00Z'
7004
- },
7005
- updatedAt: {
7006
- format: 'date-time',
7741
+ description:
7742
+ 'Converted total in base currency (omitted when FX missing for all currencies in this category)',
7743
+ example: '1200.00'
7744
+ }
7745
+ },
7746
+ required: ['category', 'totalExpense', 'sharePct', 'balanceByCurrency']
7747
+ } as const;
7748
+
7749
+ export const $ExpensesByCategorySummaryDto = {
7750
+ type: 'object',
7751
+ properties: {
7752
+ totalExpense: {
7007
7753
  type: 'string',
7008
- description: 'Last update timestamp',
7009
- example: '2024-11-03T10:00:00Z'
7754
+ description:
7755
+ 'Total across all categories, converted (convertible categories only); expense totals when flow=expense, income totals when flow=income',
7756
+ example: '5000.00'
7757
+ },
7758
+ categoryCount: {
7759
+ type: 'number',
7760
+ description: 'Number of categories',
7761
+ example: 8
7010
7762
  }
7011
7763
  },
7012
- required: [
7013
- 'id',
7014
- 'userId',
7015
- 'currency',
7016
- 'quoteCurrency',
7017
- 'amount',
7018
- 'date',
7019
- 'meta',
7020
- 'createdAt',
7021
- 'updatedAt'
7022
- ]
7764
+ required: ['totalExpense', 'categoryCount']
7023
7765
  } as const;
7024
7766
 
7025
- export const $PriceListResponseDto = {
7767
+ export const $ExpensesByCategoryResponseDto = {
7026
7768
  type: 'object',
7027
7769
  properties: {
7028
- items: {
7029
- description: 'List of prices',
7770
+ period: {
7771
+ type: 'string',
7772
+ description: 'Period requested',
7773
+ example: '1m'
7774
+ },
7775
+ baseCurrency: {
7776
+ type: 'string',
7777
+ description: 'Base currency for converted values',
7778
+ example: 'CNY'
7779
+ },
7780
+ groups: {
7781
+ description:
7782
+ 'Expense groups by functional category, sorted by converted total desc',
7030
7783
  type: 'array',
7031
7784
  items: {
7032
- $ref: '#/components/schemas/PriceResponseDto'
7785
+ $ref: '#/components/schemas/CategoryGroupDto'
7786
+ }
7787
+ },
7788
+ summary: {
7789
+ description: 'Summary statistics',
7790
+ allOf: [
7791
+ {
7792
+ $ref: '#/components/schemas/ExpensesByCategorySummaryDto'
7793
+ }
7794
+ ]
7795
+ },
7796
+ warnings: {
7797
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
7798
+ type: 'array',
7799
+ items: {
7800
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
7033
7801
  }
7034
- },
7035
- total: {
7036
- type: 'number',
7037
- description: 'Total number of prices',
7038
- example: 42
7039
7802
  }
7040
7803
  },
7041
- required: ['items', 'total']
7804
+ required: ['period', 'baseCurrency', 'groups', 'summary']
7042
7805
  } as const;
7043
7806
 
7044
- export const $UpdateBeanPriceDto = {
7807
+ export const $MonetaryDto = {
7045
7808
  type: 'object',
7046
7809
  properties: {
7047
- currency: {
7048
- type: 'string',
7049
- description: 'Currency being priced'
7050
- },
7051
- quoteCurrency: {
7052
- type: 'string',
7053
- description: 'Quote currency (pricing currency)'
7054
- },
7055
7810
  amount: {
7056
- type: 'number',
7057
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
7058
- minimum: 0
7811
+ type: 'string',
7812
+ description: 'Amount (Decimal string)',
7813
+ example: '3000'
7059
7814
  },
7060
- date: {
7815
+ currency: {
7061
7816
  type: 'string',
7062
- description: 'Price date (ISO 8601 format)'
7817
+ description: 'ISO 4217 currency',
7818
+ example: 'USD'
7063
7819
  },
7064
- metadata: {
7820
+ baseCcyEquivalent: {
7065
7821
  type: 'object',
7066
- description: 'Metadata'
7822
+ description: 'Converted to user base currency (Decimal string)',
7823
+ example: '21600',
7824
+ nullable: true
7067
7825
  }
7068
- }
7826
+ },
7827
+ required: ['amount', 'currency']
7069
7828
  } as const;
7070
7829
 
7071
- export const $CurrencyBalanceDto = {
7830
+ export const $CurrentPriceDto = {
7072
7831
  type: 'object',
7073
7832
  properties: {
7833
+ amount: {
7834
+ type: 'string',
7835
+ description: 'Price amount (Decimal string)',
7836
+ example: '250'
7837
+ },
7074
7838
  currency: {
7075
7839
  type: 'string',
7076
- description: 'ISO 4217 currency code',
7077
- example: 'CNY'
7840
+ description: 'Price currency (ISO 4217)',
7841
+ example: 'USD'
7078
7842
  },
7079
- balance: {
7843
+ date: {
7080
7844
  type: 'string',
7081
- description: 'Balance amount',
7082
- example: '500000.00'
7845
+ description: 'Price date (ISO 8601)',
7846
+ example: '2024-06-01'
7847
+ },
7848
+ source: {
7849
+ type: 'string',
7850
+ description: 'Price source',
7851
+ example: 'USER_OVERRIDE',
7852
+ enum: ['USER_OVERRIDE', 'OPENBB_EQUITY', 'OPENBB_CURRENCY']
7083
7853
  }
7084
7854
  },
7085
- required: ['currency', 'balance']
7855
+ required: ['amount', 'currency', 'date', 'source']
7086
7856
  } as const;
7087
7857
 
7088
- export const $TimeSeriesPointDto = {
7858
+ export const $FxRateDto = {
7089
7859
  type: 'object',
7090
7860
  properties: {
7091
- date: {
7861
+ from: {
7092
7862
  type: 'string',
7093
- description: 'Date in YYYY-MM-DD format',
7094
- example: '2024-06-15'
7863
+ example: 'USD'
7095
7864
  },
7096
- value: {
7865
+ to: {
7097
7866
  type: 'string',
7098
- description: 'Value at this date (in base currency)',
7099
- example: '500000.00'
7867
+ example: 'CNY'
7100
7868
  },
7101
- change: {
7102
- type: 'object',
7103
- description: 'Change from previous point',
7104
- example: '5000.00'
7869
+ rate: {
7870
+ type: 'string',
7871
+ description: 'FX rate (Decimal string)',
7872
+ example: '7.2'
7105
7873
  },
7106
- byCurrency: {
7107
- description: 'Multi-currency breakdown for this point',
7108
- type: 'array',
7109
- items: {
7110
- $ref: '#/components/schemas/CurrencyBalanceDto'
7111
- }
7874
+ date: {
7875
+ type: 'string',
7876
+ description: 'Rate date (ISO 8601)',
7877
+ example: '2024-01-15'
7112
7878
  }
7113
7879
  },
7114
- required: ['date', 'value']
7880
+ required: ['from', 'to', 'rate', 'date']
7115
7881
  } as const;
7116
7882
 
7117
- export const $TrendSummaryDto = {
7883
+ export const $HoldingPnlRowDto = {
7118
7884
  type: 'object',
7119
7885
  properties: {
7120
- startValue: {
7886
+ accountId: {
7121
7887
  type: 'string',
7122
- description: 'Value at start of period',
7123
- example: '450000.00'
7888
+ description: 'Account UUID',
7889
+ example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
7124
7890
  },
7125
- endValue: {
7891
+ accountPath: {
7126
7892
  type: 'string',
7127
- description: 'Value at end of period',
7128
- example: '500000.00'
7893
+ description: 'Full account path',
7894
+ example: 'Assets:US:Broker:AAPL'
7129
7895
  },
7130
- totalChange: {
7896
+ accountCcy: {
7897
+ type: 'object',
7898
+ description: 'Account settlement currency (ISO 4217), from cost currency',
7899
+ nullable: true,
7900
+ example: 'USD'
7901
+ },
7902
+ brokerType: {
7903
+ type: 'object',
7904
+ description: 'Broker type derived from Platform.type',
7905
+ nullable: true,
7906
+ example: 'broker'
7907
+ },
7908
+ symbol: {
7131
7909
  type: 'string',
7132
- description: 'Total change over period',
7133
- example: '50000.00'
7910
+ description: 'Commodity symbol',
7911
+ example: 'AAPL'
7134
7912
  },
7135
- totalChangePercentage: {
7913
+ chartToken: {
7136
7914
  type: 'string',
7137
- description: 'Total change percentage',
7138
- example: '+11.11%'
7915
+ description: 'Chart segment token (libs/common resolver)',
7916
+ example: 'equity',
7917
+ enum: ['equity', 'fund', 'bond', 'cash', 'other']
7918
+ },
7919
+ assetClass: {
7920
+ type: 'string',
7921
+ example: 'EQUITY'
7922
+ },
7923
+ assetSubClass: {
7924
+ type: 'object',
7925
+ nullable: true,
7926
+ example: 'STOCK'
7927
+ },
7928
+ units: {
7929
+ type: 'string',
7930
+ description: 'Net held units (Decimal string)',
7931
+ example: '12'
7932
+ },
7933
+ averageCostPerUnit: {
7934
+ description:
7935
+ 'Average cost per unit; null when cost currency conflicts or no cost',
7936
+ nullable: true,
7937
+ allOf: [
7938
+ {
7939
+ $ref: '#/components/schemas/MonetaryDto'
7940
+ }
7941
+ ]
7942
+ },
7943
+ costBasis: {
7944
+ description: 'Cost basis of held units',
7945
+ nullable: true,
7946
+ allOf: [
7947
+ {
7948
+ $ref: '#/components/schemas/MonetaryDto'
7949
+ }
7950
+ ]
7951
+ },
7952
+ marketValue: {
7953
+ description: 'Market value at asOf price',
7954
+ nullable: true,
7955
+ allOf: [
7956
+ {
7957
+ $ref: '#/components/schemas/MonetaryDto'
7958
+ }
7959
+ ]
7960
+ },
7961
+ currentPrice: {
7962
+ description: 'Price used for market value',
7963
+ nullable: true,
7964
+ allOf: [
7965
+ {
7966
+ $ref: '#/components/schemas/CurrentPriceDto'
7967
+ }
7968
+ ]
7969
+ },
7970
+ unrealizedPnlBase: {
7971
+ type: 'object',
7972
+ description:
7973
+ 'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
7974
+ nullable: true,
7975
+ example: '6000'
7976
+ },
7977
+ unrealizedPnlPct: {
7978
+ type: 'object',
7979
+ description: 'Unrealized P&L % (Decimal string)',
7980
+ nullable: true,
7981
+ example: '25'
7982
+ },
7983
+ costFxRate: {
7984
+ description: 'Historical FX rate applied to cost basis',
7985
+ nullable: true,
7986
+ allOf: [
7987
+ {
7988
+ $ref: '#/components/schemas/FxRateDto'
7989
+ }
7990
+ ]
7991
+ },
7992
+ marketFxRate: {
7993
+ description: 'FX rate applied to market value',
7994
+ nullable: true,
7995
+ allOf: [
7996
+ {
7997
+ $ref: '#/components/schemas/FxRateDto'
7998
+ }
7999
+ ]
8000
+ },
8001
+ pctOfInvestedAssets: {
8002
+ type: 'object',
8003
+ description:
8004
+ 'Share of invested assets % (Decimal string); only for invested chartTokens',
8005
+ nullable: true,
8006
+ example: '40'
8007
+ },
8008
+ realizedPnl: {
8009
+ description:
8010
+ 'Cumulative realized P&L on sold lots (asOf-date cutoff); null when the method has no applicable sells, a sell lacks a price, or any required FX rate is missing (never-mix). When a sell spans multiple currencies (cross-currency sale), amount and currency reflect the base currency; baseCcyEquivalent is always the authoritative dual-FX figure',
8011
+ nullable: true,
8012
+ allOf: [
8013
+ {
8014
+ $ref: '#/components/schemas/MonetaryDto'
8015
+ }
8016
+ ]
7139
8017
  }
7140
8018
  },
7141
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
8019
+ required: [
8020
+ 'accountId',
8021
+ 'accountPath',
8022
+ 'symbol',
8023
+ 'chartToken',
8024
+ 'assetClass',
8025
+ 'units'
8026
+ ]
7142
8027
  } as const;
7143
8028
 
7144
- export const $MultiCurrencyPointDto = {
8029
+ export const $HoldingPnlWarningDto = {
7145
8030
  type: 'object',
7146
8031
  properties: {
7147
- date: {
8032
+ type: {
7148
8033
  type: 'string',
7149
- description: 'Date in YYYY-MM-DD format',
7150
- example: '2024-06-15'
8034
+ description: 'Warning type',
8035
+ example: 'MISSING_COST_FX_RATE',
8036
+ enum: [
8037
+ 'MISSING_COST_FX_RATE',
8038
+ 'MISSING_MARKET_FX_RATE',
8039
+ 'MISSING_SALE_PRICE',
8040
+ 'MISSING_REALIZED_FX_RATE',
8041
+ 'OVERSOLD_LOTS',
8042
+ 'NO_PRICE',
8043
+ 'MIXED_COST_CURRENCY'
8044
+ ]
7151
8045
  },
7152
- byCurrency: {
7153
- description: 'Balances by currency',
7154
- type: 'array',
7155
- items: {
7156
- $ref: '#/components/schemas/CurrencyBalanceDto'
7157
- }
8046
+ symbol: {
8047
+ type: 'object',
8048
+ nullable: true
8049
+ },
8050
+ accountId: {
8051
+ type: 'object',
8052
+ nullable: true
8053
+ },
8054
+ currency: {
8055
+ type: 'object',
8056
+ nullable: true
7158
8057
  }
7159
8058
  },
7160
- required: ['date', 'byCurrency']
8059
+ required: ['type']
7161
8060
  } as const;
7162
8061
 
7163
- export const $PortfolioTrendsResponseDto = {
8062
+ export const $HoldingPnlResponseDto = {
7164
8063
  type: 'object',
7165
8064
  properties: {
7166
- series: {
7167
- description: 'Time series data points',
7168
- type: 'array',
7169
- items: {
7170
- $ref: '#/components/schemas/TimeSeriesPointDto'
7171
- }
7172
- },
7173
- summary: {
7174
- description: 'Period summary',
7175
- allOf: [
7176
- {
7177
- $ref: '#/components/schemas/TrendSummaryDto'
7178
- }
7179
- ]
7180
- },
7181
- period: {
7182
- type: 'string',
7183
- description: 'Period requested',
7184
- example: '6m'
7185
- },
7186
- granularity: {
8065
+ asOfDate: {
7187
8066
  type: 'string',
7188
- description: 'Data granularity',
7189
- example: 'month'
8067
+ example: '2026-07-08'
7190
8068
  },
7191
- currency: {
8069
+ baseCurrency: {
7192
8070
  type: 'string',
7193
- description: 'Base currency for converted values',
7194
8071
  example: 'CNY'
7195
8072
  },
7196
- byCurrency: {
8073
+ method: {
8074
+ type: 'string',
7197
8075
  description:
7198
- 'Multi-currency time series (each point has currency breakdown)',
8076
+ 'Realized-P&L lot-matching method (FIFO or average). Unrealized cost basis remains average regardless of this value (#473).',
8077
+ enum: ['average', 'FIFO'],
8078
+ example: 'average'
8079
+ },
8080
+ rows: {
7199
8081
  type: 'array',
7200
8082
  items: {
7201
- $ref: '#/components/schemas/MultiCurrencyPointDto'
8083
+ $ref: '#/components/schemas/HoldingPnlRowDto'
7202
8084
  }
7203
8085
  },
7204
8086
  warnings: {
7205
- description: 'Exchange rate warnings',
7206
8087
  type: 'array',
7207
8088
  items: {
7208
- $ref: '#/components/schemas/ExchangeRateWarningDto'
8089
+ $ref: '#/components/schemas/HoldingPnlWarningDto'
7209
8090
  }
7210
8091
  }
7211
8092
  },
7212
- required: ['series', 'summary', 'period', 'granularity', 'currency']
8093
+ required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
7213
8094
  } as const;
7214
8095
 
7215
- export const $CashFlowPointDto = {
8096
+ export const $AnonymousLoginDto = {
7216
8097
  type: 'object',
7217
8098
  properties: {
7218
- month: {
7219
- type: 'string',
7220
- description: 'Month key (YYYY-MM)',
7221
- example: '2024-03'
7222
- },
7223
- income: {
7224
- type: 'string',
7225
- description: 'Income in base currency (absolute, converted)',
7226
- example: '10000.00'
7227
- },
7228
- expense: {
7229
- type: 'string',
7230
- description: 'Expense in base currency (absolute, converted)',
7231
- example: '5000.00'
7232
- },
7233
- netSavings: {
8099
+ accessToken: {
7234
8100
  type: 'string',
7235
- description: 'netSavings = income − expense (savings positive)',
7236
- example: '5000.00'
8101
+ description: 'Access token for anonymous login'
7237
8102
  }
7238
8103
  },
7239
- required: ['month', 'income', 'expense', 'netSavings']
8104
+ required: ['accessToken']
7240
8105
  } as const;
7241
8106
 
7242
- export const $CashFlowTrendSummaryDto = {
8107
+ export const $AnonymousLoginResponseDto = {
7243
8108
  type: 'object',
7244
8109
  properties: {
7245
- totalIncome: {
7246
- type: 'string',
7247
- description: 'Total income across the period',
7248
- example: '60000.00'
7249
- },
7250
- totalExpense: {
7251
- type: 'string',
7252
- description: 'Total expense across the period',
7253
- example: '30000.00'
7254
- },
7255
- totalNetSavings: {
7256
- type: 'string',
7257
- description: 'income − expense across the period',
7258
- example: '30000.00'
7259
- },
7260
- averageMonthlyNetSavings: {
8110
+ authToken: {
7261
8111
  type: 'string',
7262
- description:
7263
- 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
7264
- example: '5000.00'
8112
+ description: 'JWT auth token',
8113
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
7265
8114
  }
7266
8115
  },
7267
- required: [
7268
- 'totalIncome',
7269
- 'totalExpense',
7270
- 'totalNetSavings',
7271
- 'averageMonthlyNetSavings'
7272
- ]
8116
+ required: ['authToken']
7273
8117
  } as const;
7274
8118
 
7275
- export const $CashFlowTrendsResponseDto = {
8119
+ export const $SymbolSearchResultDto = {
7276
8120
  type: 'object',
7277
8121
  properties: {
7278
- series: {
7279
- description:
7280
- 'Monthly cash-flow series (fixed N-month window, zero-filled)',
7281
- type: 'array',
7282
- items: {
7283
- $ref: '#/components/schemas/CashFlowPointDto'
7284
- }
8122
+ symbol: {
8123
+ type: 'string',
8124
+ example: 'AAPL'
7285
8125
  },
7286
- summary: {
7287
- description: 'Period totals',
7288
- allOf: [
7289
- {
7290
- $ref: '#/components/schemas/CashFlowTrendSummaryDto'
7291
- }
7292
- ]
8126
+ name: {
8127
+ type: 'object',
8128
+ example: 'Apple Inc.',
8129
+ nullable: true
7293
8130
  },
7294
- period: {
7295
- type: 'string',
7296
- description: 'Period requested',
7297
- example: '6m'
8131
+ exchange: {
8132
+ type: 'object',
8133
+ example: 'US',
8134
+ nullable: true
7298
8135
  },
7299
- granularity: {
7300
- type: 'string',
7301
- description: 'Data granularity (v1 returns month buckets)',
7302
- example: 'month'
8136
+ assetType: {
8137
+ type: 'object',
8138
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8139
+ example: 'stock',
8140
+ nullable: true
7303
8141
  },
7304
- currency: {
7305
- type: 'string',
7306
- description: 'Base currency for converted values',
7307
- example: 'CNY'
8142
+ assetClass: {
8143
+ type: 'object',
8144
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8145
+ example: 'EQUITY',
8146
+ nullable: true
7308
8147
  },
7309
- warnings: {
7310
- description: 'Exchange rate warnings (e.g. missing rate for a currency)',
7311
- type: 'array',
7312
- items: {
7313
- $ref: '#/components/schemas/ExchangeRateWarningDto'
7314
- }
8148
+ assetSubClass: {
8149
+ type: 'object',
8150
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8151
+ example: 'STOCK',
8152
+ nullable: true
8153
+ },
8154
+ currency: {
8155
+ type: 'object',
8156
+ description: 'Trading currency (extra_data or inferred from exchange)',
8157
+ example: 'USD',
8158
+ nullable: true
7315
8159
  }
7316
8160
  },
7317
- required: ['series', 'summary', 'period', 'granularity', 'currency']
7318
- } as const;
7319
-
7320
- export const $GenerateSnapshotBody = {
7321
- type: 'object',
7322
- properties: {}
7323
- } as const;
7324
-
7325
- export const $GenerateSnapshotResponse = {
7326
- type: 'object',
7327
- properties: {}
7328
- } as const;
7329
-
7330
- export const $BackfillSnapshotsBody = {
7331
- type: 'object',
7332
- properties: {}
7333
- } as const;
7334
-
7335
- export const $BackfillSnapshotsResponse = {
7336
- type: 'object',
7337
- properties: {}
8161
+ required: ['symbol']
7338
8162
  } as const;
7339
8163
 
7340
- export const $AnonymousLoginDto = {
8164
+ export const $SymbolQuoteDto = {
7341
8165
  type: 'object',
7342
8166
  properties: {
7343
- accessToken: {
8167
+ symbol: {
7344
8168
  type: 'string',
7345
- description: 'Access token for anonymous login'
8169
+ example: 'AAPL'
8170
+ },
8171
+ name: {
8172
+ type: 'object',
8173
+ example: 'Apple Inc.',
8174
+ nullable: true
8175
+ },
8176
+ exchange: {
8177
+ type: 'object',
8178
+ example: 'US',
8179
+ nullable: true
8180
+ },
8181
+ assetType: {
8182
+ type: 'object',
8183
+ description: 'OpenBB asset_type',
8184
+ example: 'stock',
8185
+ nullable: true
8186
+ },
8187
+ assetClass: {
8188
+ type: 'object',
8189
+ description: 'IGN asset class',
8190
+ example: 'EQUITY',
8191
+ nullable: true
8192
+ },
8193
+ assetSubClass: {
8194
+ type: 'object',
8195
+ description: 'IGN asset sub-class',
8196
+ example: 'STOCK',
8197
+ nullable: true
8198
+ },
8199
+ currency: {
8200
+ type: 'object',
8201
+ description: 'Trading currency (extra_data or inferred from exchange)',
8202
+ example: 'USD',
8203
+ nullable: true
8204
+ },
8205
+ price: {
8206
+ type: 'object',
8207
+ description: 'Latest price (Decimal string)',
8208
+ example: '189.84',
8209
+ nullable: true
8210
+ },
8211
+ priceDate: {
8212
+ type: 'object',
8213
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
8214
+ example: '2026-08-05',
8215
+ nullable: true
8216
+ },
8217
+ changePercent: {
8218
+ type: 'object',
8219
+ description:
8220
+ 'Change vs previous close, in percentage points (1.7 == 1.7%). openbb stores change_percent as a normalized decimal; this exposes percentage points for frontend convenience.',
8221
+ example: 1.7,
8222
+ nullable: true
8223
+ },
8224
+ prevClose: {
8225
+ type: 'object',
8226
+ description: 'Previous close (Decimal string)',
8227
+ nullable: true
8228
+ },
8229
+ open: {
8230
+ type: 'object',
8231
+ description: 'Day open (Decimal string)',
8232
+ nullable: true
8233
+ },
8234
+ high: {
8235
+ type: 'object',
8236
+ description: 'Day high (Decimal string)',
8237
+ nullable: true
8238
+ },
8239
+ low: {
8240
+ type: 'object',
8241
+ description: 'Day low (Decimal string)',
8242
+ nullable: true
8243
+ },
8244
+ volume: {
8245
+ type: 'object',
8246
+ description: 'Day volume (Decimal string)',
8247
+ nullable: true
8248
+ },
8249
+ yearHigh: {
8250
+ type: 'object',
8251
+ description: '52-week high (Decimal string)',
8252
+ nullable: true
8253
+ },
8254
+ yearLow: {
8255
+ type: 'object',
8256
+ description: '52-week low (Decimal string)',
8257
+ nullable: true
7346
8258
  }
7347
- },
7348
- required: ['accessToken']
8259
+ }
7349
8260
  } as const;