@firela/api-types 0.0.0-canary.a85ace93 → 0.0.0-canary.a97b40c5

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,30 @@ 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
+ displayName: {
55
+ type: 'string',
56
+ description:
57
+ 'User-set display name override (omit/null = keep the derived name)',
58
+ nullable: true,
59
+ maxLength: 50,
60
+ example: 'Salary card'
61
+ },
62
+ openDirectiveMeta: {
66
63
  type: 'object',
67
- description: 'Additional metadata',
64
+ description:
65
+ 'Open directive metadata (NOT an opening-balance amount — use the opening-balance endpoint)',
68
66
  example: {
69
67
  branch: 'Downtown',
70
68
  accountNumber: '1234'
@@ -76,7 +74,7 @@ export const $CreateAccountDto = {
76
74
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
77
75
  }
78
76
  },
79
- required: ['path', 'openDate']
77
+ required: ['path']
80
78
  } as const;
81
79
 
82
80
  export const $AccountResponseDto = {
@@ -90,13 +88,7 @@ export const $AccountResponseDto = {
90
88
  path: {
91
89
  type: 'string',
92
90
  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: '工资卡'
91
+ example: 'Assets:CN:ICBC:Checking'
100
92
  },
101
93
  type: {
102
94
  type: 'string',
@@ -104,6 +96,49 @@ export const $AccountResponseDto = {
104
96
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
105
97
  example: 'Assets'
106
98
  },
99
+ assetSubClass: {
100
+ type: 'string',
101
+ description:
102
+ '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.',
103
+ enum: [
104
+ 'DEPOSIT',
105
+ 'CASH',
106
+ 'MONEY_MARKET_FUND',
107
+ 'STOCK',
108
+ 'ETF',
109
+ 'MUTUAL_FUND',
110
+ 'EQUITY_COMPENSATION',
111
+ 'GOVERNMENT_BOND',
112
+ 'CORPORATE_BOND',
113
+ 'BOND_FUND',
114
+ 'PRIMARY_RESIDENCE',
115
+ 'INVESTMENT_PROPERTY',
116
+ 'REIT',
117
+ 'GOLD',
118
+ 'SILVER',
119
+ 'PRECIOUS_METAL',
120
+ 'PRECIOUS_METAL_FUND',
121
+ 'COMMODITY',
122
+ 'COMMODITY_FUND',
123
+ 'CRYPTOCURRENCY',
124
+ 'RETIREMENT_ACCOUNT',
125
+ 'HEALTH_ACCOUNT',
126
+ 'EDUCATION_ACCOUNT',
127
+ 'INSURANCE',
128
+ 'PRIVATE_EQUITY',
129
+ 'HEDGE_FUND',
130
+ 'COLLECTIBLES',
131
+ 'MORTGAGE',
132
+ 'STUDENT_LOAN',
133
+ 'CREDIT_CARD',
134
+ 'PERSONAL_LOAN',
135
+ 'ACCOUNTS_PAYABLE',
136
+ 'TAX_PAYABLE',
137
+ 'OTHER'
138
+ ],
139
+ nullable: true,
140
+ example: 'STOCK'
141
+ },
107
142
  status: {
108
143
  type: 'string',
109
144
  description: 'Account status',
@@ -145,32 +180,33 @@ export const $AccountResponseDto = {
145
180
  templatePath: {
146
181
  type: 'string',
147
182
  description: 'Template path reference',
148
- example: 'Assets:CN:Bank:ICBC:Checking'
183
+ example: 'Assets:CN:Checking'
149
184
  },
150
185
  isCustom: {
151
186
  type: 'boolean',
152
187
  description: 'Whether this is a custom (user-created) account',
153
188
  example: false
154
189
  },
155
- i18nKey: {
190
+ displayName: {
156
191
  type: 'string',
157
- description: 'i18n key for display name',
158
- example: 'account.assets.cn.bank.icbc.checking'
192
+ description:
193
+ 'Display name with precedence: user-set name (#762) > ADR-0114 localized name > path leaf (read-time projection)',
194
+ example: 'Checking'
159
195
  },
160
196
  icon: {
161
197
  type: 'string',
162
198
  description: 'Icon identifier',
163
199
  example: 'bank-icbc'
164
200
  },
165
- openMeta: {
201
+ openDirectiveMeta: {
166
202
  type: 'object',
167
- description: 'Account metadata',
203
+ description: 'Open directive metadata (ADR-0115 Decision 9)',
168
204
  example: {
169
205
  branch: 'Downtown'
170
206
  }
171
207
  },
172
208
  platformId: {
173
- type: 'object',
209
+ type: 'string',
174
210
  description: 'Platform ID (null if unbound)',
175
211
  example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
176
212
  },
@@ -192,7 +228,6 @@ export const $AccountResponseDto = {
192
228
  required: [
193
229
  'id',
194
230
  'path',
195
- 'displayName',
196
231
  'type',
197
232
  'status',
198
233
  'openDate',
@@ -225,11 +260,6 @@ export const $AccountListResponseDto = {
225
260
  export const $UpdateAccountDto = {
226
261
  type: 'object',
227
262
  properties: {
228
- displayName: {
229
- type: 'string',
230
- description: 'Display name to distinguish accounts at the same path',
231
- example: '招行工资卡'
232
- },
233
263
  currencies: {
234
264
  description: 'Allowed currencies (null = no restriction)',
235
265
  example: ['CNY', 'USD'],
@@ -251,19 +281,15 @@ export const $UpdateAccountDto = {
251
281
  'NONE'
252
282
  ]
253
283
  },
254
- i18nKey: {
255
- type: 'string',
256
- description: 'i18n key for display name',
257
- example: 'account.custom.mybank'
258
- },
259
284
  icon: {
260
285
  type: 'string',
261
286
  description: 'Icon identifier',
262
287
  example: 'bank-custom'
263
288
  },
264
- openMeta: {
289
+ openDirectiveMeta: {
265
290
  type: 'object',
266
- description: 'Additional metadata (merged with existing)',
291
+ description:
292
+ 'Open directive metadata (merged with existing; NOT an opening-balance amount)',
267
293
  example: {
268
294
  branch: 'Uptown'
269
295
  }
@@ -310,13 +336,47 @@ export const $ReopenAccountDto = {
310
336
  }
311
337
  } as const;
312
338
 
339
+ export const $CreateOpeningBalanceDto = {
340
+ type: 'object',
341
+ properties: {
342
+ amount: {
343
+ type: 'number',
344
+ description: 'Opening balance amount (non-negative)',
345
+ example: 1000
346
+ },
347
+ currency: {
348
+ type: 'string',
349
+ description: 'Currency code',
350
+ example: 'CNY'
351
+ },
352
+ date: {
353
+ format: 'date-time',
354
+ type: 'string',
355
+ description: 'Opening-balance date (defaults to now)',
356
+ example: '2024-01-01'
357
+ }
358
+ },
359
+ required: ['amount', 'currency']
360
+ } as const;
361
+
362
+ export const $OpeningBalanceResultDto = {
363
+ type: 'object',
364
+ properties: {
365
+ transactionId: {
366
+ type: 'string',
367
+ description: 'Created opening-balance transaction id.'
368
+ }
369
+ },
370
+ required: ['transactionId']
371
+ } as const;
372
+
313
373
  export const $AccountStandardResponseDto = {
314
374
  type: 'object',
315
375
  properties: {
316
376
  path: {
317
377
  type: 'string',
318
378
  description: 'Account path (hierarchical, colon-separated)',
319
- example: 'Assets:CN:Bank:ICBC:Checking'
379
+ example: 'Assets:CN:Checking'
320
380
  },
321
381
  type: {
322
382
  type: 'string',
@@ -324,23 +384,45 @@ export const $AccountStandardResponseDto = {
324
384
  enum: ['Assets', 'Liabilities', 'Income', 'Expenses', 'Equity'],
325
385
  example: 'Assets'
326
386
  },
327
- i18nKey: {
328
- type: 'string',
329
- description: 'i18n key for localized display name',
330
- example: 'account.assets.cn.bank.icbc.checking'
331
- },
332
387
  name: {
333
388
  type: 'string',
334
- description: 'Short localized display name',
389
+ description:
390
+ 'Short display name. Universal rows project to the request locale (Accept-Language); regional rows keep the authored native name — mixed-language by design (ADR-0131 class P vs class A).',
335
391
  example: 'Housing Fund'
336
392
  },
393
+ aliases: {
394
+ description:
395
+ 'Authored market-language alternative names delivered verbatim (not localized copy, not xlf-managed, not locale-projected). Flat string[] per ADR-0129 D1; ADR-0131 class A.',
396
+ example: ['Alipay', 'WeChat Pay'],
397
+ type: 'array',
398
+ items: {
399
+ type: 'string'
400
+ }
401
+ },
402
+ searchTerms: {
403
+ description:
404
+ 'Locale-projected search synonyms (e.g. the zh bank-card / debit-card everyday terms for the checking account). Pure-locale projection — absent when the locale has no seeded synonyms; English fallback rides the authored aliases field. Search-only vocabulary, not the NLP routing corpus (#698, ADR-0131 fourth-class adjudication).',
405
+ example: ['yinhangka', 'jiejika'],
406
+ type: 'array',
407
+ items: {
408
+ type: 'string'
409
+ }
410
+ },
411
+ currency: {
412
+ type: 'string',
413
+ description:
414
+ 'Product denomination as a 3-letter ISO 4217 code, authored market data delivered verbatim (not localized, not xlf-managed). ADR-0131 class A. Absent = single-currency not asserted — consumers fall back to their own region currency (#714).',
415
+ example: 'HKD'
416
+ },
337
417
  description: {
338
418
  type: 'string',
339
- description: 'Account description (stable semantics only)',
419
+ description:
420
+ 'Account description (stable semantics only). Mixed-language contract: universal rows project to the request locale via the accountDesc xlf axis with an en fallback (ADR-0131 class P, ADR-0132; unseeded locales falling back to English are expected); regional rows deliver the authored market language (ADR-0131 class A, verbatim, never xlf-managed).',
340
421
  example: 'ICBC checking account for daily transactions'
341
422
  },
342
423
  tags: {
343
- description: 'Account tags for categorization',
424
+ description:
425
+ 'Account tags for categorization — structured metadata delivered verbatim (not localized, not xlf-managed). ADR-0131 class A.',
344
426
  example: ['bank', 'checking', 'primary'],
345
427
  type: 'array',
346
428
  items: {
@@ -351,9 +433,47 @@ export const $AccountStandardResponseDto = {
351
433
  type: 'string',
352
434
  description: 'Icon identifier for UI display',
353
435
  example: 'bank-icbc'
436
+ },
437
+ productCategory: {
438
+ type: 'string',
439
+ description:
440
+ 'Onboarding product category (coarse grouping derived from assetSubClass)',
441
+ enum: [
442
+ 'cash',
443
+ 'investment',
444
+ 'credit_card',
445
+ 'loan',
446
+ 'payable_tax',
447
+ 'other'
448
+ ],
449
+ example: 'investment'
450
+ },
451
+ assetClass: {
452
+ type: 'string',
453
+ description:
454
+ 'Asset class (LIQUIDITY/EQUITY/.../LIABILITY), derived at read time from classification rules',
455
+ enum: [
456
+ 'LIQUIDITY',
457
+ 'EQUITY',
458
+ 'FIXED_INCOME',
459
+ 'PRECIOUS_METALS',
460
+ 'COMMODITY',
461
+ 'INSURANCE',
462
+ 'ALTERNATIVE_INVESTMENT',
463
+ 'PERSONAL_ASSETS',
464
+ 'LIABILITY',
465
+ 'REAL_ESTATE',
466
+ 'INDEX'
467
+ ]
468
+ },
469
+ assetSubClass: {
470
+ type: 'string',
471
+ description:
472
+ 'Asset sub-class (product type, derived at read time from classification rules)',
473
+ example: 'STOCK'
354
474
  }
355
475
  },
356
- required: ['path', 'type', 'i18nKey', 'description', 'tags', 'icon']
476
+ required: ['path', 'type', 'description', 'tags', 'icon', 'productCategory']
357
477
  } as const;
358
478
 
359
479
  export const $AccountStandardListResponseDto = {
@@ -383,18 +503,13 @@ export const $AccountStandardListResponseDto = {
383
503
  export const $TemplateMetadataDto = {
384
504
  type: 'object',
385
505
  properties: {
386
- extendable: {
387
- type: 'boolean',
388
- description: 'Whether this path can be extended',
389
- example: true
390
- },
391
506
  rootType: {
392
507
  type: 'string',
393
508
  description: 'Root account type',
394
509
  example: 'Assets'
395
510
  }
396
511
  },
397
- required: ['extendable', 'rootType']
512
+ required: ['rootType']
398
513
  } as const;
399
514
 
400
515
  export const $TemplateMetadataResponseDto = {
@@ -419,7 +534,10 @@ export const $RegionConfigDto = {
419
534
  },
420
535
  locale: {
421
536
  type: 'string',
422
- example: 'de-DE'
537
+ example: 'de-DE',
538
+ pattern: '^[a-z]{2,8}-[A-Z]{2}$',
539
+ description:
540
+ "Region-qualified BCP-47 tag whose region subtag equals the region's own ISO 3166-1 code (e.g., ja-JP, zh-CN, zh-HK)"
423
541
  }
424
542
  },
425
543
  required: ['currency', 'dateFormat', 'locale']
@@ -432,6 +550,12 @@ export const $RegionInfoDto = {
432
550
  type: 'string',
433
551
  example: 'de'
434
552
  },
553
+ open: {
554
+ type: 'boolean',
555
+ example: true,
556
+ description:
557
+ 'Whether the region is open (has a ready regional account template). Not-yet-open regions still return identity metadata and degrade to the universal-only catalog.'
558
+ },
435
559
  displayName: {
436
560
  type: 'string',
437
561
  example: 'Germany'
@@ -450,7 +574,7 @@ export const $RegionInfoDto = {
450
574
  $ref: '#/components/schemas/RegionConfigDto'
451
575
  }
452
576
  },
453
- required: ['code', 'displayName', 'chain', 'config']
577
+ required: ['code', 'open', 'displayName', 'chain', 'config']
454
578
  } as const;
455
579
 
456
580
  export const $RegionsMetadataResponseDto = {
@@ -466,6 +590,66 @@ export const $RegionsMetadataResponseDto = {
466
590
  required: ['regions']
467
591
  } as const;
468
592
 
593
+ export const $CostSpecDto = {
594
+ type: 'object',
595
+ properties: {
596
+ mode: {
597
+ type: 'string',
598
+ enum: ['per-unit', 'total', 'date', 'label', 'auto'],
599
+ description: 'Cost specification mode (mirrors engine CostSpec)'
600
+ },
601
+ numberPerUnit: {
602
+ type: 'string',
603
+ description: 'Per-unit cost (required when mode is "per-unit")',
604
+ example: '240'
605
+ },
606
+ totalNumber: {
607
+ type: 'string',
608
+ description: 'Total cost for all units (required when mode is "total")',
609
+ example: '12000'
610
+ },
611
+ currency: {
612
+ type: 'string',
613
+ description: 'Cost currency (required in all modes)',
614
+ example: 'USD'
615
+ },
616
+ date: {
617
+ type: 'string',
618
+ description:
619
+ 'Lot acquisition date, ISO 8601 (required when mode is "date")',
620
+ example: '2024-01-15'
621
+ },
622
+ label: {
623
+ type: 'string',
624
+ description:
625
+ 'Lot label (required when mode is "label"; optional tag in buy modes)'
626
+ },
627
+ merge: {
628
+ type: 'boolean',
629
+ description: 'Merge lots for AVERAGE booking (mode: auto)'
630
+ }
631
+ },
632
+ required: ['mode', 'currency']
633
+ } as const;
634
+
635
+ export const $AmountDto = {
636
+ type: 'object',
637
+ properties: {
638
+ number: {
639
+ type: 'string',
640
+ description:
641
+ 'Amount as decimal string (max 15 integer + 15 decimal digits)',
642
+ example: '170.50'
643
+ },
644
+ currency: {
645
+ type: 'string',
646
+ description: 'Currency/commodity code',
647
+ example: 'USD'
648
+ }
649
+ },
650
+ required: ['number', 'currency']
651
+ } as const;
652
+
469
653
  export const $CreatePostingDto = {
470
654
  type: 'object',
471
655
  properties: {
@@ -473,7 +657,7 @@ export const $CreatePostingDto = {
473
657
  type: 'string',
474
658
  description:
475
659
  'Account name in Beancount format (must start with uppercase, colon-separated)',
476
- example: 'Assets:Bank:Checking'
660
+ example: 'Assets:Checking'
477
661
  },
478
662
  units: {
479
663
  type: 'string',
@@ -495,6 +679,33 @@ export const $CreatePostingDto = {
495
679
  example: {
496
680
  'tax-lot': 'Q1-2024'
497
681
  }
682
+ },
683
+ cost: {
684
+ description:
685
+ 'Cost basis (Beancount `{...}`). Maps to engine costSpec. Required for commodity holdings so they carry a monetary weight that can balance.',
686
+ example: {
687
+ mode: 'per-unit',
688
+ numberPerUnit: '240',
689
+ currency: 'USD'
690
+ },
691
+ allOf: [
692
+ {
693
+ $ref: '#/components/schemas/CostSpecDto'
694
+ }
695
+ ]
696
+ },
697
+ price: {
698
+ description:
699
+ 'Price annotation (Beancount `@...`). Maps to engine price. Used for valuation; cost takes priority for balance weight.',
700
+ example: {
701
+ number: '170',
702
+ currency: 'USD'
703
+ },
704
+ allOf: [
705
+ {
706
+ $ref: '#/components/schemas/AmountDto'
707
+ }
708
+ ]
498
709
  }
499
710
  },
500
711
  required: ['account']
@@ -573,24 +784,59 @@ export const $CreateTransactionDto = {
573
784
  required: ['date', 'narration', 'postings']
574
785
  } as const;
575
786
 
787
+ export const $CostDetailDto = {
788
+ type: 'object',
789
+ properties: {
790
+ number: {
791
+ type: 'string',
792
+ description: 'Per-unit cost basis (mirrors engine Cost.number)',
793
+ example: '240'
794
+ },
795
+ currency: {
796
+ type: 'string',
797
+ description: 'Cost currency',
798
+ example: 'USD'
799
+ },
800
+ date: {
801
+ type: 'string',
802
+ description: 'Lot acquisition date (ISO yyyy-mm-dd)',
803
+ example: '2024-01-15'
804
+ },
805
+ label: {
806
+ type: 'string',
807
+ description: 'Lot label',
808
+ example: 'lot-2024-01'
809
+ }
810
+ }
811
+ } as const;
812
+
576
813
  export const $PostingResponseDto = {
577
814
  type: 'object',
578
815
  properties: {
579
816
  account: {
580
817
  type: 'string',
581
818
  description: 'Account name',
582
- example: 'Assets:Bank:Checking'
819
+ example: 'Assets:Checking'
583
820
  },
584
821
  units: {
585
822
  type: 'string',
586
823
  description:
587
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
824
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
588
825
  example: '100.50'
589
826
  },
590
827
  currency: {
591
828
  type: 'string',
592
829
  description: 'Currency',
593
830
  example: 'USD'
831
+ },
832
+ cost: {
833
+ description:
834
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
835
+ allOf: [
836
+ {
837
+ $ref: '#/components/schemas/CostDetailDto'
838
+ }
839
+ ]
594
840
  }
595
841
  },
596
842
  required: ['account']
@@ -936,12 +1182,12 @@ export const $PostingDetailDto = {
936
1182
  account: {
937
1183
  type: 'string',
938
1184
  description: 'Fully-qualified Beancount account path',
939
- example: 'Assets:Bank:Checking'
1185
+ example: 'Assets:Checking'
940
1186
  },
941
1187
  units: {
942
1188
  type: 'string',
943
1189
  description:
944
- 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned.',
1190
+ 'Amount as decimal string. Typed optional but always present in responses: interpolation fills any MISSING posting before it is persisted or returned. Carries the raw Beancount sign (credit-normal accounts such as Income post negative — the accounting truth, ADR-0126); renderers must not infer economic semantics from this sign.',
945
1191
  example: '100.50'
946
1192
  },
947
1193
  currency: {
@@ -964,6 +1210,15 @@ export const $PostingDetailDto = {
964
1210
  description: 'Cost date',
965
1211
  example: '2024-01-15'
966
1212
  },
1213
+ cost: {
1214
+ description:
1215
+ 'Booking-resolved cost (mirrors engine Cost). Undefined when the posting has no cost basis.',
1216
+ allOf: [
1217
+ {
1218
+ $ref: '#/components/schemas/CostDetailDto'
1219
+ }
1220
+ ]
1221
+ },
967
1222
  priceAmount: {
968
1223
  type: 'string',
969
1224
  description: 'Price amount',
@@ -1116,49 +1371,299 @@ export const $TransactionDetailDto = {
1116
1371
  ]
1117
1372
  } as const;
1118
1373
 
1119
- export const $TransactionListResponseDto = {
1374
+ export const $TransactionListItemDto = {
1120
1375
  type: 'object',
1121
1376
  properties: {
1122
- data: {
1123
- description: 'List of transactions',
1124
- type: 'array',
1125
- items: {
1126
- $ref: '#/components/schemas/TransactionDetailDto'
1127
- }
1128
- },
1129
- total: {
1130
- type: 'number',
1131
- description: 'Total count of matching transactions',
1132
- example: 100
1133
- },
1134
- limit: {
1135
- type: 'number',
1136
- description: 'Number of items per page',
1137
- example: 20
1377
+ id: {
1378
+ type: 'string',
1379
+ description: 'Transaction ID',
1380
+ example: 'clh1234567890abcdef'
1138
1381
  },
1139
- offset: {
1140
- type: 'number',
1141
- description: 'Number of items skipped',
1142
- example: 0
1143
- }
1144
- },
1145
- required: ['data', 'total', 'limit', 'offset']
1146
- } as const;
1147
-
1148
- export const $TagSuggestionDto = {
1149
- type: 'object',
1150
- properties: {
1151
- tag: {
1382
+ date: {
1152
1383
  type: 'string',
1153
- description: 'Tag name',
1154
- example: 'Monthly'
1384
+ description: 'Transaction date',
1385
+ example: '2024-11-28'
1155
1386
  },
1156
- count: {
1157
- type: 'number',
1158
- description: 'Usage count across ACTIVE transactions',
1159
- example: 12
1160
- }
1161
- },
1387
+ flag: {
1388
+ type: 'string',
1389
+ description: 'Transaction flag',
1390
+ enum: [
1391
+ 'CLEARED',
1392
+ 'PENDING',
1393
+ 'PADDING',
1394
+ 'SUMMARIZE',
1395
+ 'TRANSFER',
1396
+ 'CONVERSIONS'
1397
+ ],
1398
+ example: 'CLEARED'
1399
+ },
1400
+ customFlag: {
1401
+ type: 'string',
1402
+ description: 'Custom flag (if not using standard flags)',
1403
+ example: 'R'
1404
+ },
1405
+ payee: {
1406
+ type: 'string',
1407
+ description: 'Payee name',
1408
+ example: 'Whole Foods Market'
1409
+ },
1410
+ narration: {
1411
+ type: 'string',
1412
+ description: 'Transaction narration',
1413
+ example: 'Grocery shopping'
1414
+ },
1415
+ tags: {
1416
+ description: 'Transaction tags',
1417
+ example: ['groceries'],
1418
+ type: 'array',
1419
+ items: {
1420
+ type: 'string'
1421
+ }
1422
+ },
1423
+ links: {
1424
+ description: 'Transaction links',
1425
+ example: ['invoice-2024-001'],
1426
+ type: 'array',
1427
+ items: {
1428
+ type: 'string'
1429
+ }
1430
+ },
1431
+ meta: {
1432
+ type: 'object',
1433
+ description: 'Transaction metadata'
1434
+ },
1435
+ status: {
1436
+ type: 'string',
1437
+ description: 'Transaction status',
1438
+ enum: ['ACTIVE', 'VOIDED', 'SUPERSEDED'],
1439
+ example: 'ACTIVE'
1440
+ },
1441
+ sourceType: {
1442
+ type: 'string',
1443
+ description:
1444
+ 'Source type (free-form string from transaction metadata, e.g. import, api)'
1445
+ },
1446
+ sourcePlatform: {
1447
+ type: 'string',
1448
+ description: 'Source platform (e.g., alipay, wechat)',
1449
+ example: 'alipay'
1450
+ },
1451
+ postings: {
1452
+ description: 'Transaction postings',
1453
+ type: 'array',
1454
+ items: {
1455
+ $ref: '#/components/schemas/PostingDetailDto'
1456
+ }
1457
+ },
1458
+ createdAt: {
1459
+ type: 'string',
1460
+ description: 'Created at timestamp',
1461
+ example: '2024-11-28T10:30:00.000Z'
1462
+ },
1463
+ voidedAt: {
1464
+ type: 'string',
1465
+ description: 'Voided at timestamp (if voided)',
1466
+ example: '2024-11-29T15:00:00.000Z'
1467
+ },
1468
+ voidedBy: {
1469
+ type: 'string',
1470
+ description: 'User ID who voided this transaction',
1471
+ example: 'clh1234567890abcdef'
1472
+ },
1473
+ correctionReason: {
1474
+ type: 'string',
1475
+ description: 'Correction reason (if voided or superseded)',
1476
+ example: 'Duplicate entry'
1477
+ },
1478
+ supersededBy: {
1479
+ type: 'string',
1480
+ description:
1481
+ 'ID of the transaction that supersedes this one (set when status=SUPERSEDED)',
1482
+ example: 'clh1234567890abcdef'
1483
+ },
1484
+ originalTxn: {
1485
+ type: 'string',
1486
+ description:
1487
+ 'ID of the transaction this one corrected/replaced (back-link on the replacement)',
1488
+ example: 'clh1234567890abcdef'
1489
+ },
1490
+ viewpointAmount: {
1491
+ type: 'string',
1492
+ description:
1493
+ 'Row amount under the request viewpoint (ADR-0126). Category viewpoint (category + flow): per-leg sign-normalized sum over the category account set (Income-root legs negated, Expenses-root identity) — positive under normal booking but NOT clamped (explicit negative expense legs and net-flip refund months stay negative). No viewpoint (plain list / search, no accountId): wallet money-flow net = raw-sign sum over cost-less Assets/Liabilities legs (income positive, expenses negative, transfers net ~0); color cue is the wallet sign (net < 0 = wealth-decreasing). Status-orthogonal: audit views match too (ADR-0128 amount-as-matching-key). Omitted under the account viewpoint (incl. dual) and for rows with no wallet leg.',
1494
+ example: '10000.00'
1495
+ },
1496
+ viewpointCurrency: {
1497
+ type: 'string',
1498
+ description:
1499
+ 'Currency of viewpointAmount. A row spanning multiple currencies takes the largest-magnitude currency group (known simplification, ADR-0126).',
1500
+ example: 'CNY'
1501
+ }
1502
+ },
1503
+ required: [
1504
+ 'id',
1505
+ 'date',
1506
+ 'narration',
1507
+ 'tags',
1508
+ 'links',
1509
+ 'status',
1510
+ 'postings',
1511
+ 'createdAt'
1512
+ ]
1513
+ } as const;
1514
+
1515
+ export const $BalanceByCurrencyDto = {
1516
+ type: 'object',
1517
+ properties: {
1518
+ currency: {
1519
+ type: 'string',
1520
+ description: 'ISO 4217 currency code',
1521
+ example: 'CNY'
1522
+ },
1523
+ balance: {
1524
+ type: 'string',
1525
+ description: 'Balance amount',
1526
+ example: '50000.00'
1527
+ }
1528
+ },
1529
+ required: ['currency', 'balance']
1530
+ } as const;
1531
+
1532
+ export const $ExchangeRateWarningDto = {
1533
+ type: 'object',
1534
+ properties: {
1535
+ type: {
1536
+ type: 'string',
1537
+ description: 'Warning type',
1538
+ example: 'MISSING_EXCHANGE_RATE'
1539
+ },
1540
+ currency: {
1541
+ type: 'string',
1542
+ description: 'Currency without exchange rate',
1543
+ example: 'EUR'
1544
+ },
1545
+ totalAmount: {
1546
+ type: 'string',
1547
+ description: 'Total amount affected',
1548
+ example: '1000.00'
1549
+ }
1550
+ },
1551
+ required: ['type', 'currency', 'totalAmount']
1552
+ } as const;
1553
+
1554
+ export const $TransactionListSummaryDto = {
1555
+ type: 'object',
1556
+ properties: {
1557
+ totalAmount: {
1558
+ type: 'string',
1559
+ description:
1560
+ 'Partial converted total in base currency (rated currencies only). Sign by viewpoint (ADR-0126): account viewpoint keeps the raw Beancount sign (income negative); category viewpoint is per-leg sign-normalized (Income legs negated, Expenses legs identity — positive under normal booking, not clamped). When warnings is non-empty this excludes currencies missing an FX rate; may be "0.00" if ALL non-base currencies lack a rate. Converted at the dateTo (or current) available rate.',
1561
+ example: '-6000.00'
1562
+ },
1563
+ currency: {
1564
+ type: 'string',
1565
+ description: 'Base currency (ISO 4217)',
1566
+ example: 'CNY'
1567
+ },
1568
+ balanceByCurrency: {
1569
+ description: 'Raw (unconverted) balance per currency',
1570
+ type: 'array',
1571
+ items: {
1572
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
1573
+ }
1574
+ },
1575
+ warnings: {
1576
+ description: 'Currencies missing an FX rate (omitted when empty)',
1577
+ type: 'array',
1578
+ items: {
1579
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
1580
+ }
1581
+ }
1582
+ },
1583
+ required: ['totalAmount', 'currency', 'balanceByCurrency']
1584
+ } as const;
1585
+
1586
+ export const $TransactionListViewpointDto = {
1587
+ type: 'object',
1588
+ properties: {
1589
+ type: {
1590
+ type: 'string',
1591
+ description:
1592
+ 'Viewpoint type (only category drill-down carries a viewpoint today)',
1593
+ enum: ['category'],
1594
+ example: 'category'
1595
+ },
1596
+ flow: {
1597
+ type: 'string',
1598
+ description: 'Flow root the category account set is restricted to',
1599
+ enum: ['income', 'expense'],
1600
+ example: 'expense'
1601
+ }
1602
+ },
1603
+ required: ['type', 'flow']
1604
+ } as const;
1605
+
1606
+ export const $TransactionListResponseDto = {
1607
+ type: 'object',
1608
+ properties: {
1609
+ data: {
1610
+ description: 'List of transactions',
1611
+ type: 'array',
1612
+ items: {
1613
+ $ref: '#/components/schemas/TransactionListItemDto'
1614
+ }
1615
+ },
1616
+ total: {
1617
+ type: 'number',
1618
+ description: 'Total count of matching transactions',
1619
+ example: 100
1620
+ },
1621
+ limit: {
1622
+ type: 'number',
1623
+ description: 'Number of items per page',
1624
+ example: 20
1625
+ },
1626
+ offset: {
1627
+ type: 'number',
1628
+ description: 'Number of items skipped',
1629
+ example: 0
1630
+ },
1631
+ summary: {
1632
+ description:
1633
+ '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.',
1634
+ allOf: [
1635
+ {
1636
+ $ref: '#/components/schemas/TransactionListSummaryDto'
1637
+ }
1638
+ ]
1639
+ },
1640
+ viewpoint: {
1641
+ description:
1642
+ 'Viewpoint metadata (ADR-0126). Present only for a single category filter (category + flow, no accountId); dual-perspective requests are viewpoint-less (raw signs, no viewpointAmount).',
1643
+ allOf: [
1644
+ {
1645
+ $ref: '#/components/schemas/TransactionListViewpointDto'
1646
+ }
1647
+ ]
1648
+ }
1649
+ },
1650
+ required: ['data', 'total', 'limit', 'offset']
1651
+ } as const;
1652
+
1653
+ export const $TagSuggestionDto = {
1654
+ type: 'object',
1655
+ properties: {
1656
+ tag: {
1657
+ type: 'string',
1658
+ description: 'Tag name',
1659
+ example: 'Monthly'
1660
+ },
1661
+ count: {
1662
+ type: 'number',
1663
+ description: 'Usage count across ACTIVE transactions',
1664
+ example: 12
1665
+ }
1666
+ },
1162
1667
  required: ['tag', 'count']
1163
1668
  } as const;
1164
1669
 
@@ -1239,7 +1744,7 @@ export const $BalanceResponseDto = {
1239
1744
  account: {
1240
1745
  type: 'string',
1241
1746
  description: 'Account name',
1242
- example: 'Assets:Bank:Checking'
1747
+ example: 'Assets:Checking'
1243
1748
  },
1244
1749
  balance: {
1245
1750
  type: 'string',
@@ -1266,7 +1771,7 @@ export const $MultiCurrencyBalanceResponseDto = {
1266
1771
  account: {
1267
1772
  type: 'string',
1268
1773
  description: 'Account name',
1269
- example: 'Assets:Bank:Checking'
1774
+ example: 'Assets:Checking'
1270
1775
  },
1271
1776
  balances: {
1272
1777
  type: 'object',
@@ -1322,7 +1827,7 @@ export const $TransactionSummaryDto = {
1322
1827
  accountName: {
1323
1828
  type: 'string',
1324
1829
  description: 'Source account name (first posting)',
1325
- example: 'Assets:Bank:Checking'
1830
+ example: 'Assets:Checking'
1326
1831
  },
1327
1832
  sourceType: {
1328
1833
  type: 'string',
@@ -1724,7 +2229,8 @@ export const $ResolveResultDto = {
1724
2229
  },
1725
2230
  resolutionId: {
1726
2231
  type: 'string',
1727
- description: 'Resolution ID for undo'
2232
+ description:
2233
+ 'Resolution ID for undo. Absent when the resolver rejected the decision (review stayed PENDING).'
1728
2234
  },
1729
2235
  canUndo: {
1730
2236
  type: 'boolean',
@@ -1742,7 +2248,7 @@ export const $ResolveResultDto = {
1742
2248
  example: 'rule_01HXK5V8N2M3P4Q5R6S7T8U9V0'
1743
2249
  }
1744
2250
  },
1745
- required: ['success', 'resolutionId', 'canUndo', 'undoDeadline']
2251
+ required: ['success']
1746
2252
  } as const;
1747
2253
 
1748
2254
  export const $UndoResultDto = {
@@ -2049,10 +2555,10 @@ export const $UpdatePayeeDto = {
2049
2555
  meta: {
2050
2556
  type: 'object',
2051
2557
  description:
2052
- 'Metadata for extended information (location, notes, contact info, etc.). Will merge with existing metadata.',
2558
+ 'Metadata for extended information (location, notes, contact info, etc.)',
2053
2559
  example: {
2054
2560
  location: 'Zhongguancun',
2055
- note: 'Updated note',
2561
+ note: 'Near subway station',
2056
2562
  favorite: true
2057
2563
  }
2058
2564
  },
@@ -2613,182 +3119,828 @@ export const $UpdateCommodityDto = {
2613
3119
  }
2614
3120
  } as const;
2615
3121
 
2616
- export const $CreateRecurringRuleDto = {
3122
+ export const $CurrencyBalanceDto = {
2617
3123
  type: 'object',
2618
3124
  properties: {
2619
- name: {
3125
+ currency: {
2620
3126
  type: 'string',
2621
- description: 'Rule name (unique per user)',
2622
- maxLength: 100
3127
+ description: 'ISO 4217 currency code',
3128
+ example: 'CNY'
2623
3129
  },
2624
- icon: {
3130
+ balance: {
2625
3131
  type: 'string',
2626
- description: 'Icon emoji',
2627
- maxLength: 10
2628
- },
2629
- frequency: {
3132
+ description: 'Balance amount',
3133
+ example: '500000.00'
3134
+ }
3135
+ },
3136
+ required: ['currency', 'balance']
3137
+ } as const;
3138
+
3139
+ export const $TimeSeriesPointDto = {
3140
+ type: 'object',
3141
+ properties: {
3142
+ date: {
2630
3143
  type: 'string',
2631
- description: 'Recurring frequency',
2632
- enum: [
2633
- 'WEEKLY',
2634
- 'BIWEEKLY',
2635
- 'MONTHLY',
2636
- 'BIMONTHLY',
2637
- 'QUARTERLY',
2638
- 'YEARLY',
2639
- 'CUSTOM'
2640
- ]
2641
- },
2642
- expectedAmount: {
2643
- type: 'number',
2644
- description: 'Expected amount (positive number)',
2645
- minimum: 0
2646
- },
2647
- expectedDay: {
2648
- type: 'number',
2649
- description: 'Expected day of month (1-31)',
2650
- minimum: 1,
2651
- maximum: 31
2652
- },
2653
- customIntervalDays: {
2654
- type: 'number',
2655
- description: 'Custom interval in days (required for CUSTOM frequency)',
2656
- minimum: 1
3144
+ description: 'Date in YYYY-MM-DD format',
3145
+ example: '2024-06-15'
2657
3146
  },
2658
- currency: {
3147
+ value: {
2659
3148
  type: 'string',
2660
- description: 'Currency code',
2661
- default: 'CNY',
2662
- maxLength: 10
3149
+ description: 'Value at this date (in base currency)',
3150
+ example: '500000.00'
2663
3151
  },
2664
- matchPayeePattern: {
3152
+ change: {
2665
3153
  type: 'string',
2666
- description: 'Payee matching pattern (supports wildcards)',
2667
- maxLength: 200
2668
- },
2669
- matchAmountTolerance: {
2670
- type: 'number',
2671
- description: 'Amount tolerance percentage (0-1)',
2672
- default: 0.075,
2673
- minimum: 0,
2674
- maximum: 1
3154
+ description: 'Change from previous point',
3155
+ example: '5000.00'
2675
3156
  },
2676
- defaultExpenseAccount: {
3157
+ assets: {
2677
3158
  type: 'string',
2678
- description: 'Default expense account for auto-create',
2679
- maxLength: 200
3159
+ description: 'Total assets at this date (in base currency)',
3160
+ example: '494338.00'
2680
3161
  },
2681
- defaultPaymentAccount: {
3162
+ liabilities: {
2682
3163
  type: 'string',
2683
- description: 'Default payment account for auto-create',
2684
- maxLength: 200
3164
+ description: 'Total liabilities at this date (in base currency)',
3165
+ example: '310098.00'
2685
3166
  },
2686
- defaultPayee: {
3167
+ byCurrency: {
3168
+ description: 'Multi-currency breakdown for this point',
3169
+ type: 'array',
3170
+ items: {
3171
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3172
+ }
3173
+ }
3174
+ },
3175
+ required: ['date', 'value']
3176
+ } as const;
3177
+
3178
+ export const $TrendSummaryDto = {
3179
+ type: 'object',
3180
+ properties: {
3181
+ startValue: {
2687
3182
  type: 'string',
2688
- description: 'Default payee for auto-create',
2689
- maxLength: 200
3183
+ description: 'Value at start of period',
3184
+ example: '450000.00'
2690
3185
  },
2691
- autoCreate: {
2692
- type: 'boolean',
2693
- description: 'Auto-create transaction when expected date arrives',
2694
- default: false
3186
+ endValue: {
3187
+ type: 'string',
3188
+ description: 'Value at end of period',
3189
+ example: '500000.00'
2695
3190
  },
2696
- startDate: {
3191
+ totalChange: {
2697
3192
  type: 'string',
2698
- description: 'Rule start date (ISO format)'
3193
+ description: 'Total change over period',
3194
+ example: '50000.00'
2699
3195
  },
2700
- endDate: {
3196
+ totalChangePercentage: {
2701
3197
  type: 'string',
2702
- description: 'Rule end date (ISO format)'
3198
+ description: 'Total change percentage',
3199
+ example: '+11.11%'
2703
3200
  }
2704
3201
  },
2705
- required: [
2706
- 'name',
2707
- 'frequency',
2708
- 'expectedAmount',
2709
- 'currency',
2710
- 'matchAmountTolerance',
2711
- 'autoCreate'
2712
- ]
3202
+ required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
2713
3203
  } as const;
2714
3204
 
2715
- export const $RecurringRuleResponseDto = {
3205
+ export const $MultiCurrencyPointDto = {
2716
3206
  type: 'object',
2717
3207
  properties: {
2718
- id: {
2719
- type: 'string',
2720
- description: 'Rule ID'
2721
- },
2722
- userId: {
3208
+ date: {
2723
3209
  type: 'string',
2724
- description: 'User ID'
3210
+ description: 'Date in YYYY-MM-DD format',
3211
+ example: '2024-06-15'
2725
3212
  },
2726
- name: {
2727
- type: 'string',
2728
- description: 'Rule name'
3213
+ byCurrency: {
3214
+ description: 'Balances by currency',
3215
+ type: 'array',
3216
+ items: {
3217
+ $ref: '#/components/schemas/CurrencyBalanceDto'
3218
+ }
3219
+ }
3220
+ },
3221
+ required: ['date', 'byCurrency']
3222
+ } as const;
3223
+
3224
+ export const $PortfolioTrendsResponseDto = {
3225
+ type: 'object',
3226
+ properties: {
3227
+ series: {
3228
+ description: 'Time series data points',
3229
+ type: 'array',
3230
+ items: {
3231
+ $ref: '#/components/schemas/TimeSeriesPointDto'
3232
+ }
2729
3233
  },
2730
- icon: {
2731
- type: 'object',
2732
- description: 'Icon emoji'
3234
+ summary: {
3235
+ description: 'Period summary',
3236
+ allOf: [
3237
+ {
3238
+ $ref: '#/components/schemas/TrendSummaryDto'
3239
+ }
3240
+ ]
2733
3241
  },
2734
- frequency: {
3242
+ period: {
2735
3243
  type: 'string',
2736
- description: 'Recurring frequency'
2737
- },
2738
- expectedAmount: {
2739
- type: 'number',
2740
- description: 'Expected amount'
2741
- },
2742
- expectedDay: {
2743
- type: 'object',
2744
- description: 'Expected day of month'
3244
+ description: 'Period requested',
3245
+ example: '6m'
2745
3246
  },
2746
- customIntervalDays: {
2747
- type: 'object',
2748
- description: 'Custom interval in days'
3247
+ granularity: {
3248
+ type: 'string',
3249
+ description: 'Data granularity',
3250
+ example: 'month'
2749
3251
  },
2750
3252
  currency: {
2751
3253
  type: 'string',
2752
- description: 'Currency code'
3254
+ description: 'Base currency for converted values',
3255
+ example: 'CNY'
2753
3256
  },
2754
- matchPayeePattern: {
2755
- type: 'object',
2756
- description: 'Payee matching pattern'
3257
+ byCurrency: {
3258
+ description:
3259
+ 'Multi-currency time series (each point has currency breakdown)',
3260
+ type: 'array',
3261
+ items: {
3262
+ $ref: '#/components/schemas/MultiCurrencyPointDto'
3263
+ }
2757
3264
  },
2758
- matchAmountTolerance: {
2759
- type: 'number',
2760
- description: 'Amount tolerance percentage'
3265
+ warnings: {
3266
+ description: 'Exchange rate warnings',
3267
+ type: 'array',
3268
+ items: {
3269
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3270
+ }
3271
+ }
3272
+ },
3273
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3274
+ } as const;
3275
+
3276
+ export const $CashFlowPointDto = {
3277
+ type: 'object',
3278
+ properties: {
3279
+ month: {
3280
+ type: 'string',
3281
+ description: 'Month key (YYYY-MM)',
3282
+ example: '2024-03'
2761
3283
  },
2762
- defaultExpenseAccount: {
2763
- type: 'object',
2764
- description: 'Default expense account'
3284
+ income: {
3285
+ type: 'string',
3286
+ description: 'Income in base currency (absolute, converted)',
3287
+ example: '10000.00'
2765
3288
  },
2766
- defaultPaymentAccount: {
2767
- type: 'object',
2768
- description: 'Default payment account'
3289
+ expense: {
3290
+ type: 'string',
3291
+ description: 'Expense in base currency (absolute, converted)',
3292
+ example: '5000.00'
2769
3293
  },
2770
- defaultPayee: {
2771
- type: 'object',
2772
- description: 'Default payee'
3294
+ netSavings: {
3295
+ type: 'string',
3296
+ description: 'netSavings = income − expense (savings positive)',
3297
+ example: '5000.00'
3298
+ }
3299
+ },
3300
+ required: ['month', 'income', 'expense', 'netSavings']
3301
+ } as const;
3302
+
3303
+ export const $CashFlowTrendSummaryDto = {
3304
+ type: 'object',
3305
+ properties: {
3306
+ totalIncome: {
3307
+ type: 'string',
3308
+ description: 'Total income across the period',
3309
+ example: '60000.00'
2773
3310
  },
2774
- isActive: {
2775
- type: 'boolean',
2776
- description: 'Whether rule is active'
3311
+ totalExpense: {
3312
+ type: 'string',
3313
+ description: 'Total expense across the period',
3314
+ example: '30000.00'
2777
3315
  },
2778
- startDate: {
3316
+ totalNetSavings: {
2779
3317
  type: 'string',
2780
- description: 'Rule start date (YYYY-MM-DD)'
3318
+ description: 'income − expense across the period',
3319
+ example: '30000.00'
2781
3320
  },
2782
- endDate: {
2783
- type: 'object',
2784
- description: 'Rule end date (YYYY-MM-DD)'
3321
+ averageMonthlyNetSavings: {
3322
+ type: 'string',
3323
+ description:
3324
+ 'totalNetSavings divided by the window length (N months, incl. zero-filled)',
3325
+ example: '5000.00'
3326
+ }
3327
+ },
3328
+ required: [
3329
+ 'totalIncome',
3330
+ 'totalExpense',
3331
+ 'totalNetSavings',
3332
+ 'averageMonthlyNetSavings'
3333
+ ]
3334
+ } as const;
3335
+
3336
+ export const $CashFlowTrendsResponseDto = {
3337
+ type: 'object',
3338
+ properties: {
3339
+ series: {
3340
+ description:
3341
+ 'Monthly cash-flow series (fixed N-month window, zero-filled)',
3342
+ type: 'array',
3343
+ items: {
3344
+ $ref: '#/components/schemas/CashFlowPointDto'
3345
+ }
3346
+ },
3347
+ summary: {
3348
+ description: 'Period totals',
3349
+ allOf: [
3350
+ {
3351
+ $ref: '#/components/schemas/CashFlowTrendSummaryDto'
3352
+ }
3353
+ ]
3354
+ },
3355
+ period: {
3356
+ type: 'string',
3357
+ description: 'Period requested',
3358
+ example: '6m'
3359
+ },
3360
+ granularity: {
3361
+ type: 'string',
3362
+ description: 'Data granularity (v1 returns month buckets)',
3363
+ example: 'month'
3364
+ },
3365
+ currency: {
3366
+ type: 'string',
3367
+ description: 'Base currency for converted values',
3368
+ example: 'CNY'
3369
+ },
3370
+ warnings: {
3371
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
3372
+ type: 'array',
3373
+ items: {
3374
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
3375
+ }
3376
+ }
3377
+ },
3378
+ required: ['series', 'summary', 'period', 'granularity', 'currency']
3379
+ } as const;
3380
+
3381
+ export const $GenerateSnapshotBody = {
3382
+ type: 'object',
3383
+ properties: {}
3384
+ } as const;
3385
+
3386
+ export const $GenerateSnapshotResponse = {
3387
+ type: 'object',
3388
+ properties: {}
3389
+ } as const;
3390
+
3391
+ export const $BackfillSnapshotsBody = {
3392
+ type: 'object',
3393
+ properties: {}
3394
+ } as const;
3395
+
3396
+ export const $BackfillSnapshotsResponse = {
3397
+ type: 'object',
3398
+ properties: {}
3399
+ } as const;
3400
+
3401
+ export const $CreateBeanPriceDto = {
3402
+ type: 'object',
3403
+ properties: {
3404
+ currency: {
3405
+ type: 'string',
3406
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3407
+ example: 'USD'
3408
+ },
3409
+ quoteCurrency: {
3410
+ type: 'string',
3411
+ description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
3412
+ example: 'CNY'
3413
+ },
3414
+ amount: {
3415
+ type: 'number',
3416
+ description:
3417
+ 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
3418
+ example: 175.5,
3419
+ minimum: 0
3420
+ },
3421
+ date: {
3422
+ type: 'string',
3423
+ description: 'Price date (ISO 8601 format)',
3424
+ example: '2024-11-05'
3425
+ },
3426
+ metadata: {
3427
+ type: 'object',
3428
+ description:
3429
+ 'Metadata (validated by Zod schema, max field lengths enforced)',
3430
+ example: {
3431
+ source: 'MANUAL',
3432
+ note: 'Bank valuation report',
3433
+ confidence: 0.95
3434
+ }
3435
+ }
3436
+ },
3437
+ required: ['currency', 'quoteCurrency', 'amount', 'date']
3438
+ } as const;
3439
+
3440
+ export const $PriceResponseDto = {
3441
+ type: 'object',
3442
+ properties: {
3443
+ id: {
3444
+ type: 'string',
3445
+ description: 'Unique identifier',
3446
+ example: 'uuid-123-456'
3447
+ },
3448
+ userId: {
3449
+ type: 'string',
3450
+ description: 'User ID (owner of the price)',
3451
+ example: 'user-123'
3452
+ },
3453
+ currency: {
3454
+ type: 'string',
3455
+ description: 'Currency being priced (e.g., USD, AAPL, BTC)',
3456
+ example: 'BTC'
3457
+ },
3458
+ quoteCurrency: {
3459
+ type: 'string',
3460
+ description: 'Quote currency (pricing currency, e.g., USD, CNY)',
3461
+ example: 'USD'
3462
+ },
3463
+ amount: {
3464
+ type: 'number',
3465
+ description:
3466
+ 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
3467
+ example: 50000
3468
+ },
3469
+ date: {
3470
+ type: 'string',
3471
+ description:
3472
+ 'Price date (ISO 8601 format). Represents the date this price was valid.',
3473
+ example: '2024-01-01',
3474
+ format: 'date'
3475
+ },
3476
+ meta: {
3477
+ type: 'object',
3478
+ description:
3479
+ 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
3480
+ example: {
3481
+ source: 'MANUAL',
3482
+ note: 'User-defined price',
3483
+ confidence: 1
3484
+ }
3485
+ },
3486
+ createdAt: {
3487
+ format: 'date-time',
3488
+ type: 'string',
3489
+ description: 'Creation timestamp',
3490
+ example: '2024-11-03T10:00:00Z'
3491
+ },
3492
+ updatedAt: {
3493
+ format: 'date-time',
3494
+ type: 'string',
3495
+ description: 'Last update timestamp',
3496
+ example: '2024-11-03T10:00:00Z'
3497
+ }
3498
+ },
3499
+ required: [
3500
+ 'id',
3501
+ 'userId',
3502
+ 'currency',
3503
+ 'quoteCurrency',
3504
+ 'amount',
3505
+ 'date',
3506
+ 'meta',
3507
+ 'createdAt',
3508
+ 'updatedAt'
3509
+ ]
3510
+ } as const;
3511
+
3512
+ export const $PriceListResponseDto = {
3513
+ type: 'object',
3514
+ properties: {
3515
+ items: {
3516
+ description: 'List of prices',
3517
+ type: 'array',
3518
+ items: {
3519
+ $ref: '#/components/schemas/PriceResponseDto'
3520
+ }
3521
+ },
3522
+ total: {
3523
+ type: 'number',
3524
+ description: 'Total number of prices',
3525
+ example: 42
3526
+ }
3527
+ },
3528
+ required: ['items', 'total']
3529
+ } as const;
3530
+
3531
+ export const $UpdateBeanPriceDto = {
3532
+ type: 'object',
3533
+ properties: {
3534
+ currency: {
3535
+ type: 'string',
3536
+ description: 'Currency being priced'
3537
+ },
3538
+ quoteCurrency: {
3539
+ type: 'string',
3540
+ description: 'Quote currency (pricing currency)'
3541
+ },
3542
+ amount: {
3543
+ type: 'number',
3544
+ description: 'Price amount (MUST be >= 0 per Beancount spec)',
3545
+ minimum: 0
3546
+ },
3547
+ date: {
3548
+ type: 'string',
3549
+ description: 'Price date (ISO 8601 format)'
3550
+ },
3551
+ metadata: {
3552
+ type: 'object',
3553
+ description: 'Metadata'
3554
+ }
3555
+ }
3556
+ } as const;
3557
+
3558
+ export const $DeleteOwnUserDto = {
3559
+ type: 'object',
3560
+ properties: {
3561
+ accessToken: {
3562
+ type: 'string',
3563
+ description: 'Access token for user verification',
3564
+ example: 'abc123xyz'
3565
+ }
3566
+ },
3567
+ required: ['accessToken']
3568
+ } as const;
3569
+
3570
+ export const $UserSettingsResponseDto = {
3571
+ type: 'object',
3572
+ properties: {
3573
+ baseCurrency: {
3574
+ type: 'string',
3575
+ description:
3576
+ 'Stored base currency choice (ISO 4217) for net-worth/report aggregation. null = user never chose; aggregates fall back to the region default at display time (#713).',
3577
+ example: 'USD',
3578
+ nullable: true
3579
+ }
3580
+ },
3581
+ required: ['baseCurrency']
3582
+ } as const;
3583
+
3584
+ export const $UserResponseDto = {
3585
+ type: 'object',
3586
+ properties: {
3587
+ id: {
3588
+ type: 'string',
3589
+ description: 'User ID'
3590
+ },
3591
+ role: {
3592
+ type: 'string',
3593
+ description: 'Assigned user role'
3594
+ },
3595
+ permissions: {
3596
+ description: 'Permission strings',
3597
+ type: 'array',
3598
+ items: {
3599
+ type: 'string'
3600
+ }
3601
+ },
3602
+ settings: {
3603
+ description: 'User settings',
3604
+ allOf: [
3605
+ {
3606
+ $ref: '#/components/schemas/UserSettingsResponseDto'
3607
+ }
3608
+ ]
3609
+ }
3610
+ },
3611
+ required: ['id', 'role', 'permissions', 'settings']
3612
+ } as const;
3613
+
3614
+ export const $SignupDto = {
3615
+ type: 'object',
3616
+ properties: {
3617
+ turnstileToken: {
3618
+ type: 'string',
3619
+ description:
3620
+ 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
3621
+ example: '0.abc123def456...'
3622
+ }
3623
+ }
3624
+ } as const;
3625
+
3626
+ export const $SignupResponseDto = {
3627
+ type: 'object',
3628
+ properties: {
3629
+ authToken: {
3630
+ type: 'string',
3631
+ description: 'JWT auth token',
3632
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
3633
+ },
3634
+ accessToken: {
3635
+ type: 'string',
3636
+ description: 'Auto-generated access token'
3637
+ },
3638
+ role: {
3639
+ type: 'string',
3640
+ description: 'Assigned user role',
3641
+ enum: ['USER', 'ADMIN', 'DEMO', 'INACTIVE', 'PAID', 'OPS']
3642
+ }
3643
+ },
3644
+ required: ['authToken', 'accessToken', 'role']
3645
+ } as const;
3646
+
3647
+ export const $UpdateUserSettingDto = {
3648
+ type: 'object',
3649
+ properties: {
3650
+ secId: {
3651
+ type: 'number',
3652
+ description: 'Security ID'
3653
+ },
3654
+ annualInterestRate: {
3655
+ type: 'number',
3656
+ description: 'Annual interest rate',
3657
+ example: 0.05
3658
+ },
3659
+ currency: {
3660
+ type: 'string',
3661
+ description: 'Currency code',
3662
+ example: 'USD'
3663
+ },
3664
+ baseCurrency: {
3665
+ type: 'string',
3666
+ description: 'Base currency code',
3667
+ example: 'USD'
3668
+ },
3669
+ benchmark: {
3670
+ type: 'string',
3671
+ description: 'Benchmark symbol',
3672
+ example: 'SPY'
3673
+ },
3674
+ colorScheme: {
3675
+ type: 'string',
3676
+ description: 'Color scheme',
3677
+ enum: ['DARK', 'LIGHT']
3678
+ },
3679
+ dateRange: {
3680
+ type: 'string',
3681
+ description: 'Date range filter',
3682
+ example: '1y'
3683
+ },
3684
+ emergencyFund: {
3685
+ type: 'number',
3686
+ description: 'Emergency fund amount',
3687
+ example: 10000
3688
+ },
3689
+ 'filters.accounts': {
3690
+ description: 'Account filter IDs',
3691
+ type: 'array',
3692
+ items: {
3693
+ type: 'string'
3694
+ }
3695
+ },
3696
+ 'filters.assetClasses': {
3697
+ description: 'Asset class filters',
3698
+ type: 'array',
3699
+ items: {
3700
+ type: 'string'
3701
+ }
3702
+ },
3703
+ 'filters.dataSource': {
3704
+ type: 'string',
3705
+ description: 'Data source filter'
3706
+ },
3707
+ 'filters.symbol': {
3708
+ type: 'string',
3709
+ description: 'Symbol filter'
3710
+ },
3711
+ 'filters.tags': {
3712
+ description: 'Tag filters',
3713
+ type: 'array',
3714
+ items: {
3715
+ type: 'string'
3716
+ }
3717
+ },
3718
+ isExperimentalFeatures: {
3719
+ type: 'boolean',
3720
+ description: 'Enable experimental features'
3721
+ },
3722
+ isRestrictedView: {
3723
+ type: 'boolean',
3724
+ description: 'Enable restricted view mode'
3725
+ },
3726
+ language: {
3727
+ type: 'string',
3728
+ description: 'Language code',
3729
+ example: 'en'
3730
+ },
3731
+ locale: {
3732
+ type: 'string',
3733
+ description: 'Locale code',
3734
+ example: 'en-US'
3735
+ },
3736
+ projectedTotalAmount: {
3737
+ type: 'number',
3738
+ description: 'Projected total amount',
3739
+ example: 1000000
3740
+ },
3741
+ retirementDate: {
3742
+ type: 'string',
3743
+ description: 'Retirement date in ISO 8601 format',
3744
+ example: '2050-01-01'
3745
+ },
3746
+ savingsRate: {
3747
+ type: 'number',
3748
+ description: 'Savings rate percentage',
3749
+ example: 0.2
3750
+ },
3751
+ viewMode: {
3752
+ type: 'string',
3753
+ description: 'View mode',
3754
+ enum: ['DEFAULT', 'ZEN']
3755
+ }
3756
+ }
3757
+ } as const;
3758
+
3759
+ export const $UpdatePropertyDto = {
3760
+ type: 'object',
3761
+ properties: {
3762
+ value: {
3763
+ type: 'string',
3764
+ description: 'Property value'
3765
+ }
3766
+ },
3767
+ required: ['value']
3768
+ } as const;
3769
+
3770
+ export const $CreateRecurringRuleDto = {
3771
+ type: 'object',
3772
+ properties: {
3773
+ name: {
3774
+ type: 'string',
3775
+ description: 'Rule name (unique per user)',
3776
+ maxLength: 100
3777
+ },
3778
+ icon: {
3779
+ type: 'string',
3780
+ description: 'Icon emoji',
3781
+ maxLength: 10
3782
+ },
3783
+ frequency: {
3784
+ type: 'string',
3785
+ description: 'Recurring frequency',
3786
+ enum: [
3787
+ 'WEEKLY',
3788
+ 'BIWEEKLY',
3789
+ 'MONTHLY',
3790
+ 'BIMONTHLY',
3791
+ 'QUARTERLY',
3792
+ 'YEARLY',
3793
+ 'CUSTOM'
3794
+ ]
3795
+ },
3796
+ expectedAmount: {
3797
+ type: 'number',
3798
+ description: 'Expected amount (positive number)',
3799
+ minimum: 0
3800
+ },
3801
+ expectedDay: {
3802
+ type: 'number',
3803
+ description: 'Expected day of month (1-31)',
3804
+ minimum: 1,
3805
+ maximum: 31
3806
+ },
3807
+ customIntervalDays: {
3808
+ type: 'number',
3809
+ description: 'Custom interval in days (required for CUSTOM frequency)',
3810
+ minimum: 1
3811
+ },
3812
+ currency: {
3813
+ type: 'string',
3814
+ description: 'Currency code',
3815
+ maxLength: 10
3816
+ },
3817
+ matchPayeePattern: {
3818
+ type: 'string',
3819
+ description: 'Payee matching pattern (supports wildcards)',
3820
+ maxLength: 200
3821
+ },
3822
+ matchAmountTolerance: {
3823
+ type: 'number',
3824
+ description: 'Amount tolerance percentage (0-1)',
3825
+ default: 0.075,
3826
+ minimum: 0,
3827
+ maximum: 1
3828
+ },
3829
+ defaultExpenseAccount: {
3830
+ type: 'string',
3831
+ description: 'Default expense account for auto-create',
3832
+ maxLength: 200
3833
+ },
3834
+ defaultPaymentAccount: {
3835
+ type: 'string',
3836
+ description: 'Default payment account for auto-create',
3837
+ maxLength: 200
3838
+ },
3839
+ defaultPayee: {
3840
+ type: 'string',
3841
+ description: 'Default payee for auto-create',
3842
+ maxLength: 200
3843
+ },
3844
+ autoCreate: {
3845
+ type: 'boolean',
3846
+ description: 'Auto-create transaction when expected date arrives',
3847
+ default: false
3848
+ },
3849
+ startDate: {
3850
+ type: 'string',
3851
+ description: 'Rule start date (ISO format)'
3852
+ },
3853
+ endDate: {
3854
+ type: 'string',
3855
+ description: 'Rule end date (ISO format)'
3856
+ }
3857
+ },
3858
+ required: [
3859
+ 'name',
3860
+ 'frequency',
3861
+ 'expectedAmount',
3862
+ 'matchAmountTolerance',
3863
+ 'autoCreate'
3864
+ ]
3865
+ } as const;
3866
+
3867
+ export const $RecurringRuleResponseDto = {
3868
+ type: 'object',
3869
+ properties: {
3870
+ id: {
3871
+ type: 'string',
3872
+ description: 'Rule ID'
3873
+ },
3874
+ userId: {
3875
+ type: 'string',
3876
+ description: 'User ID'
3877
+ },
3878
+ name: {
3879
+ type: 'string',
3880
+ description: 'Rule name'
3881
+ },
3882
+ icon: {
3883
+ type: 'string',
3884
+ description: 'Icon emoji'
3885
+ },
3886
+ frequency: {
3887
+ type: 'string',
3888
+ description: 'Recurring frequency'
3889
+ },
3890
+ expectedAmount: {
3891
+ type: 'number',
3892
+ description: 'Expected amount'
3893
+ },
3894
+ expectedDay: {
3895
+ type: 'number',
3896
+ description: 'Expected day of month'
3897
+ },
3898
+ customIntervalDays: {
3899
+ type: 'number',
3900
+ description: 'Custom interval in days'
3901
+ },
3902
+ currency: {
3903
+ type: 'string',
3904
+ description: 'Currency code'
3905
+ },
3906
+ matchPayeePattern: {
3907
+ type: 'string',
3908
+ description: 'Payee matching pattern'
3909
+ },
3910
+ matchAmountTolerance: {
3911
+ type: 'number',
3912
+ description: 'Amount tolerance percentage'
3913
+ },
3914
+ defaultExpenseAccount: {
3915
+ type: 'string',
3916
+ description: 'Default expense account'
3917
+ },
3918
+ defaultPaymentAccount: {
3919
+ type: 'string',
3920
+ description: 'Default payment account'
3921
+ },
3922
+ defaultPayee: {
3923
+ type: 'string',
3924
+ description: 'Default payee'
3925
+ },
3926
+ isActive: {
3927
+ type: 'boolean',
3928
+ description: 'Whether rule is active'
3929
+ },
3930
+ startDate: {
3931
+ type: 'string',
3932
+ description: 'Rule start date (YYYY-MM-DD)'
3933
+ },
3934
+ endDate: {
3935
+ type: 'string',
3936
+ description: 'Rule end date (YYYY-MM-DD)'
2785
3937
  },
2786
3938
  autoCreate: {
2787
3939
  type: 'boolean',
2788
3940
  description: 'Auto-create transaction on expected date'
2789
3941
  },
2790
3942
  lastOccurrence: {
2791
- type: 'object',
3943
+ type: 'string',
2792
3944
  description: 'Last matched occurrence date (YYYY-MM-DD)'
2793
3945
  },
2794
3946
  totalCount: {
@@ -2870,7 +4022,7 @@ export const $RecurringRuleWithStatsResponseDto = {
2870
4022
  description: 'Rule name'
2871
4023
  },
2872
4024
  icon: {
2873
- type: 'object',
4025
+ type: 'string',
2874
4026
  description: 'Icon emoji'
2875
4027
  },
2876
4028
  frequency: {
@@ -2882,11 +4034,11 @@ export const $RecurringRuleWithStatsResponseDto = {
2882
4034
  description: 'Expected amount'
2883
4035
  },
2884
4036
  expectedDay: {
2885
- type: 'object',
4037
+ type: 'number',
2886
4038
  description: 'Expected day of month'
2887
4039
  },
2888
4040
  customIntervalDays: {
2889
- type: 'object',
4041
+ type: 'number',
2890
4042
  description: 'Custom interval in days'
2891
4043
  },
2892
4044
  currency: {
@@ -2894,7 +4046,7 @@ export const $RecurringRuleWithStatsResponseDto = {
2894
4046
  description: 'Currency code'
2895
4047
  },
2896
4048
  matchPayeePattern: {
2897
- type: 'object',
4049
+ type: 'string',
2898
4050
  description: 'Payee matching pattern'
2899
4051
  },
2900
4052
  matchAmountTolerance: {
@@ -2902,15 +4054,15 @@ export const $RecurringRuleWithStatsResponseDto = {
2902
4054
  description: 'Amount tolerance percentage'
2903
4055
  },
2904
4056
  defaultExpenseAccount: {
2905
- type: 'object',
4057
+ type: 'string',
2906
4058
  description: 'Default expense account'
2907
4059
  },
2908
4060
  defaultPaymentAccount: {
2909
- type: 'object',
4061
+ type: 'string',
2910
4062
  description: 'Default payment account'
2911
4063
  },
2912
4064
  defaultPayee: {
2913
- type: 'object',
4065
+ type: 'string',
2914
4066
  description: 'Default payee'
2915
4067
  },
2916
4068
  isActive: {
@@ -2922,7 +4074,7 @@ export const $RecurringRuleWithStatsResponseDto = {
2922
4074
  description: 'Rule start date (YYYY-MM-DD)'
2923
4075
  },
2924
4076
  endDate: {
2925
- type: 'object',
4077
+ type: 'string',
2926
4078
  description: 'Rule end date (YYYY-MM-DD)'
2927
4079
  },
2928
4080
  autoCreate: {
@@ -2930,7 +4082,7 @@ export const $RecurringRuleWithStatsResponseDto = {
2930
4082
  description: 'Auto-create transaction on expected date'
2931
4083
  },
2932
4084
  lastOccurrence: {
2933
- type: 'object',
4085
+ type: 'string',
2934
4086
  description: 'Last matched occurrence date (YYYY-MM-DD)'
2935
4087
  },
2936
4088
  totalCount: {
@@ -2956,7 +4108,7 @@ export const $RecurringRuleWithStatsResponseDto = {
2956
4108
  description: 'Number of overdue expected transactions'
2957
4109
  },
2958
4110
  nextExpectedDate: {
2959
- type: 'object',
4111
+ type: 'string',
2960
4112
  description: 'Next expected date (YYYY-MM-DD)'
2961
4113
  },
2962
4114
  totalAmount: {
@@ -2972,11 +4124,11 @@ export const $RecurringRuleWithStatsResponseDto = {
2972
4124
  description: 'Number of matched transactions'
2973
4125
  },
2974
4126
  firstDate: {
2975
- type: 'object',
4127
+ type: 'string',
2976
4128
  description: 'First matched transaction date (YYYY-MM-DD)'
2977
4129
  },
2978
4130
  lastDate: {
2979
- type: 'object',
4131
+ type: 'string',
2980
4132
  description: 'Last matched transaction date (YYYY-MM-DD)'
2981
4133
  },
2982
4134
  variance: {
@@ -3017,7 +4169,7 @@ export const $UpdateRecurringRuleDto = {
3017
4169
  properties: {
3018
4170
  name: {
3019
4171
  type: 'string',
3020
- description: 'Rule name',
4172
+ description: 'Rule name (unique per user)',
3021
4173
  maxLength: 100
3022
4174
  },
3023
4175
  icon: {
@@ -3040,7 +4192,7 @@ export const $UpdateRecurringRuleDto = {
3040
4192
  },
3041
4193
  expectedAmount: {
3042
4194
  type: 'number',
3043
- description: 'Expected amount',
4195
+ description: 'Expected amount (positive number)',
3044
4196
  minimum: 0
3045
4197
  },
3046
4198
  expectedDay: {
@@ -3049,11 +4201,6 @@ export const $UpdateRecurringRuleDto = {
3049
4201
  minimum: 1,
3050
4202
  maximum: 31
3051
4203
  },
3052
- customIntervalDays: {
3053
- type: 'number',
3054
- description: 'Custom interval in days',
3055
- minimum: 1
3056
- },
3057
4204
  currency: {
3058
4205
  type: 'string',
3059
4206
  description: 'Currency code',
@@ -3061,41 +4208,48 @@ export const $UpdateRecurringRuleDto = {
3061
4208
  },
3062
4209
  matchPayeePattern: {
3063
4210
  type: 'string',
3064
- description: 'Payee matching pattern',
4211
+ description: 'Payee matching pattern (supports wildcards)',
3065
4212
  maxLength: 200
3066
4213
  },
3067
4214
  matchAmountTolerance: {
3068
4215
  type: 'number',
3069
4216
  description: 'Amount tolerance percentage (0-1)',
4217
+ default: 0.075,
3070
4218
  minimum: 0,
3071
4219
  maximum: 1
3072
4220
  },
3073
4221
  defaultExpenseAccount: {
3074
4222
  type: 'string',
3075
- description: 'Default expense account',
4223
+ description: 'Default expense account for auto-create',
3076
4224
  maxLength: 200
3077
4225
  },
3078
4226
  defaultPaymentAccount: {
3079
4227
  type: 'string',
3080
- description: 'Default payment account',
4228
+ description: 'Default payment account for auto-create',
3081
4229
  maxLength: 200
3082
4230
  },
3083
4231
  defaultPayee: {
3084
4232
  type: 'string',
3085
- description: 'Default payee',
4233
+ description: 'Default payee for auto-create',
3086
4234
  maxLength: 200
3087
4235
  },
3088
4236
  autoCreate: {
3089
4237
  type: 'boolean',
3090
- description: 'Auto-create transaction'
3091
- },
3092
- isActive: {
3093
- type: 'boolean',
3094
- description: 'Rule active status'
4238
+ description: 'Auto-create transaction when expected date arrives',
4239
+ default: false
3095
4240
  },
3096
4241
  endDate: {
3097
4242
  type: 'string',
3098
4243
  description: 'Rule end date (ISO format)'
4244
+ },
4245
+ customIntervalDays: {
4246
+ type: 'number',
4247
+ description: 'Custom interval in days',
4248
+ minimum: 1
4249
+ },
4250
+ isActive: {
4251
+ type: 'boolean',
4252
+ description: 'Rule active status'
3099
4253
  }
3100
4254
  }
3101
4255
  } as const;
@@ -3108,7 +4262,7 @@ export const $ExpectedTransactionRuleDto = {
3108
4262
  description: 'Rule name'
3109
4263
  },
3110
4264
  icon: {
3111
- type: 'object',
4265
+ type: 'string',
3112
4266
  description: 'Rule icon'
3113
4267
  },
3114
4268
  frequency: {
@@ -3151,15 +4305,15 @@ export const $ExpectedTransactionResponseDto = {
3151
4305
  description: 'Status (PENDING, COMPLETED, SKIPPED)'
3152
4306
  },
3153
4307
  matchedTransactionId: {
3154
- type: 'object',
4308
+ type: 'string',
3155
4309
  description: 'Matched transaction ID'
3156
4310
  },
3157
4311
  matchedAt: {
3158
- type: 'object',
4312
+ type: 'string',
3159
4313
  description: 'Match timestamp (ISO 8601)'
3160
4314
  },
3161
4315
  matchConfidence: {
3162
- type: 'object',
4316
+ type: 'number',
3163
4317
  description: 'Match confidence score (0-1)'
3164
4318
  },
3165
4319
  isOverdue: {
@@ -3943,7 +5097,8 @@ export const $UpdateTransactionRuleDto = {
3943
5097
  },
3944
5098
  matchLogic: {
3945
5099
  type: 'string',
3946
- enum: ['OR', 'AND']
5100
+ enum: ['OR', 'AND'],
5101
+ default: 'OR'
3947
5102
  },
3948
5103
  amountMin: {
3949
5104
  type: 'number',
@@ -3955,229 +5110,543 @@ export const $UpdateTransactionRuleDto = {
3955
5110
  minimum: 0,
3956
5111
  description: 'Maximum transaction amount (inclusive)'
3957
5112
  },
3958
- priority: {
5113
+ priority: {
5114
+ type: 'number',
5115
+ default: 50,
5116
+ minimum: 0,
5117
+ maximum: 1000
5118
+ },
5119
+ additionalTags: {
5120
+ items: {
5121
+ type: 'array'
5122
+ },
5123
+ maxItems: 20,
5124
+ type: 'array'
5125
+ },
5126
+ additionalMetadata: {
5127
+ type: 'object'
5128
+ },
5129
+ enabled: {
5130
+ type: 'boolean',
5131
+ description: 'Enable or disable the rule'
5132
+ }
5133
+ }
5134
+ } as const;
5135
+
5136
+ export const $TestRuleDto = {
5137
+ type: 'object',
5138
+ properties: {
5139
+ narration: {
5140
+ type: 'string',
5141
+ minLength: 1,
5142
+ maxLength: 500
5143
+ },
5144
+ payee: {
5145
+ type: 'string',
5146
+ maxLength: 200
5147
+ },
5148
+ categoryAccount: {
5149
+ type: 'string',
5150
+ maxLength: 200
5151
+ },
5152
+ amount: {
5153
+ type: 'number'
5154
+ },
5155
+ currency: {
5156
+ type: 'string',
5157
+ maxLength: 10
5158
+ }
5159
+ },
5160
+ required: ['narration']
5161
+ } as const;
5162
+
5163
+ export const $TestRuleResponseDto = {
5164
+ type: 'object',
5165
+ properties: {
5166
+ ruleId: {
5167
+ type: 'string',
5168
+ description: 'Rule ID that was tested'
5169
+ },
5170
+ matches: {
5171
+ type: 'boolean',
5172
+ description: 'Whether the rule matched the test data'
5173
+ },
5174
+ confidence: {
5175
+ type: 'number',
5176
+ description: 'Match confidence score (0-1)',
5177
+ example: 0.85
5178
+ },
5179
+ matchDetails: {
5180
+ type: 'object',
5181
+ description: 'Details of which fields matched',
5182
+ example: {
5183
+ narration: true,
5184
+ payee: false,
5185
+ categoryAccount: false
5186
+ }
5187
+ }
5188
+ },
5189
+ required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5190
+ } as const;
5191
+
5192
+ export const $CategoryCatalogEntryDto = {
5193
+ type: 'object',
5194
+ properties: {
5195
+ slug: {
5196
+ type: 'string',
5197
+ description: 'Category slug (single source-of-truth)',
5198
+ example: 'food'
5199
+ },
5200
+ scenario: {
5201
+ type: 'string',
5202
+ description: 'Display scenario group (maps to frontend picker _scenario)',
5203
+ enum: [
5204
+ 'expense',
5205
+ 'income',
5206
+ 'investment',
5207
+ 'banking',
5208
+ 'transfer',
5209
+ 'payment'
5210
+ ],
5211
+ example: 'expense'
5212
+ },
5213
+ icon: {
5214
+ type: 'string',
5215
+ description: 'Lucide icon name',
5216
+ example: 'utensils'
5217
+ },
5218
+ regions: {
5219
+ description: "Applicable regions ('*' = all, 'cn' = CN-only)",
5220
+ example: ['*'],
5221
+ type: 'array',
5222
+ items: {
5223
+ type: 'string'
5224
+ }
5225
+ }
5226
+ },
5227
+ required: ['slug', 'scenario', 'icon', 'regions']
5228
+ } as const;
5229
+
5230
+ export const $CategoryCatalogListResponseDto = {
5231
+ type: 'object',
5232
+ properties: {
5233
+ items: {
5234
+ description: 'Category entries (region-scoped, query-filtered)',
5235
+ type: 'array',
5236
+ items: {
5237
+ $ref: '#/components/schemas/CategoryCatalogEntryDto'
5238
+ }
5239
+ },
5240
+ total: {
5241
+ type: 'number',
5242
+ description:
5243
+ 'Total category entries for the region (before query filtering)',
5244
+ example: 30
5245
+ },
5246
+ region: {
5247
+ type: 'string',
5248
+ description: 'Region code',
5249
+ example: 'cn'
5250
+ }
5251
+ },
5252
+ required: ['items', 'total', 'region']
5253
+ } as const;
5254
+
5255
+ export const $CreateBeanEventDto = {
5256
+ type: 'object',
5257
+ properties: {
5258
+ date: {
5259
+ type: 'string',
5260
+ description: 'Life event date (ISO 8601)',
5261
+ example: '2024-03-15'
5262
+ },
5263
+ type: {
5264
+ type: 'string',
5265
+ description:
5266
+ 'Life event type (e.g., "employer", "location", "marital-status") — user-defined, no enum constraint at engine layer',
5267
+ example: 'employer'
5268
+ },
5269
+ description: {
5270
+ type: 'string',
5271
+ description:
5272
+ 'Life event description. Empty string is a VALID value (distinct from absence).',
5273
+ example: 'Acme Corp'
5274
+ },
5275
+ meta: {
5276
+ type: 'object',
5277
+ description:
5278
+ 'Product-side metadata (lives in BeanEvent.meta JSON, never in engine Event fields)',
5279
+ example: {
5280
+ note: 'Promotion'
5281
+ }
5282
+ }
5283
+ },
5284
+ required: ['date', 'type', 'description']
5285
+ } as const;
5286
+
5287
+ export const $EventResponseDto = {
5288
+ type: 'object',
5289
+ properties: {
5290
+ id: {
5291
+ type: 'string',
5292
+ description: 'Unique identifier',
5293
+ example: 'uuid-123-456'
5294
+ },
5295
+ userId: {
5296
+ type: 'string',
5297
+ description: 'User ID (owner of the life event)',
5298
+ example: 'user-123'
5299
+ },
5300
+ date: {
5301
+ type: 'string',
5302
+ description: 'Life event date (ISO 8601 format)',
5303
+ example: '2024-03-15',
5304
+ format: 'date'
5305
+ },
5306
+ type: {
5307
+ type: 'string',
5308
+ description:
5309
+ 'Life event type (user-defined, e.g., "employer", "location")',
5310
+ example: 'employer'
5311
+ },
5312
+ description: {
5313
+ type: 'string',
5314
+ description:
5315
+ 'Life event description. May be an empty string (a valid value distinct from absence).',
5316
+ example: 'Acme Corp'
5317
+ },
5318
+ meta: {
5319
+ type: 'object',
5320
+ description: 'Product-side metadata (free-form JSON)',
5321
+ example: {
5322
+ note: 'Promotion'
5323
+ }
5324
+ },
5325
+ createdAt: {
5326
+ format: 'date-time',
5327
+ type: 'string',
5328
+ description: 'Creation timestamp',
5329
+ example: '2024-03-15T10:00:00Z'
5330
+ },
5331
+ updatedAt: {
5332
+ format: 'date-time',
5333
+ type: 'string',
5334
+ description:
5335
+ 'Last update timestamp. Also emitted as the ETag response header for If-Match optimistic concurrency.',
5336
+ example: '2024-03-15T10:00:00Z'
5337
+ }
5338
+ },
5339
+ required: [
5340
+ 'id',
5341
+ 'userId',
5342
+ 'date',
5343
+ 'type',
5344
+ 'description',
5345
+ 'meta',
5346
+ 'createdAt',
5347
+ 'updatedAt'
5348
+ ]
5349
+ } as const;
5350
+
5351
+ export const $EventListResponseDto = {
5352
+ type: 'object',
5353
+ properties: {
5354
+ items: {
5355
+ description: 'List of life events',
5356
+ type: 'array',
5357
+ items: {
5358
+ $ref: '#/components/schemas/EventResponseDto'
5359
+ }
5360
+ },
5361
+ total: {
3959
5362
  type: 'number',
3960
- minimum: 0,
3961
- maximum: 1000
5363
+ description: 'Total number of life events matching the query',
5364
+ example: 42
5365
+ }
5366
+ },
5367
+ required: ['items', 'total']
5368
+ } as const;
5369
+
5370
+ export const $UpdateBeanEventDto = {
5371
+ type: 'object',
5372
+ properties: {
5373
+ date: {
5374
+ type: 'string',
5375
+ description: 'Life event date (ISO 8601)'
3962
5376
  },
3963
- enabled: {
3964
- type: 'boolean',
3965
- description: 'Enable or disable the rule'
5377
+ type: {
5378
+ type: 'string',
5379
+ description: 'Life event type (user-defined)'
3966
5380
  },
3967
- additionalTags: {
3968
- items: {
3969
- type: 'array'
3970
- },
3971
- maxItems: 20,
3972
- type: 'array'
5381
+ description: {
5382
+ type: 'string',
5383
+ description:
5384
+ 'Life event description. Empty string is a VALID value (distinct from absence).'
3973
5385
  },
3974
- additionalMetadata: {
3975
- type: 'object'
5386
+ meta: {
5387
+ type: 'object',
5388
+ description: 'Product-side metadata (free-form JSON)'
3976
5389
  }
3977
5390
  }
3978
5391
  } as const;
3979
5392
 
3980
- export const $TestRuleDto = {
5393
+ export const $OnboardingAccountDto = {
3981
5394
  type: 'object',
3982
5395
  properties: {
3983
- narration: {
5396
+ path: {
3984
5397
  type: 'string',
3985
- minLength: 1,
3986
- maxLength: 500
5398
+ description:
5399
+ 'Account path (Assets/Liabilities only; format validated by the account service)',
5400
+ example: 'Assets:Checking'
3987
5401
  },
3988
- payee: {
5402
+ currency: {
3989
5403
  type: 'string',
3990
- maxLength: 200
5404
+ description: 'ISO 4217 currency code (3 letters)',
5405
+ example: 'USD'
3991
5406
  },
3992
- categoryAccount: {
5407
+ openingBalance: {
3993
5408
  type: 'string',
3994
- maxLength: 200
5409
+ description:
5410
+ 'Opening balance as a non-negative Decimal string (e.g. "1000.00")',
5411
+ example: '1000.00'
3995
5412
  },
3996
- amount: {
3997
- type: 'number'
5413
+ platformId: {
5414
+ type: 'string',
5415
+ description:
5416
+ 'Platform ID to bind the account to (references Platform.id); omit for unbound',
5417
+ example: 'c98e5d4a-2f71-4a5a-bb3c-92c9f231d5e2'
3998
5418
  },
3999
- currency: {
5419
+ displayName: {
4000
5420
  type: 'string',
4001
- maxLength: 10
5421
+ description:
5422
+ 'User-set display name override (omit/null = keep the derived name)',
5423
+ nullable: true,
5424
+ maxLength: 50,
5425
+ example: 'Salary card'
4002
5426
  }
4003
5427
  },
4004
- required: ['narration']
5428
+ required: ['path', 'currency']
4005
5429
  } as const;
4006
5430
 
4007
- export const $TestRuleResponseDto = {
5431
+ export const $OnboardingDto = {
4008
5432
  type: 'object',
4009
5433
  properties: {
4010
- ruleId: {
4011
- type: 'string',
4012
- description: 'Rule ID that was tested'
5434
+ accounts: {
5435
+ description: 'Asset/Liability accounts to register with opening balances',
5436
+ type: 'array',
5437
+ items: {
5438
+ $ref: '#/components/schemas/OnboardingAccountDto'
5439
+ }
4013
5440
  },
4014
- matches: {
5441
+ skipAssetRegistration: {
4015
5442
  type: 'boolean',
4016
- description: 'Whether the rule matched the test data'
4017
- },
4018
- confidence: {
4019
- type: 'number',
4020
- description: 'Match confidence score (0-1)',
4021
- example: 0.85
4022
- },
4023
- matchDetails: {
4024
- type: 'object',
4025
- description: 'Details of which fields matched',
4026
- example: {
4027
- narration: true,
4028
- payee: false,
4029
- categoryAccount: false
4030
- }
5443
+ description:
5444
+ 'Skip asset registration; only bootstrap the core account set',
5445
+ default: false
4031
5446
  }
4032
- },
4033
- required: ['ruleId', 'matches', 'confidence', 'matchDetails']
5447
+ }
4034
5448
  } as const;
4035
5449
 
4036
- export const $DeleteOwnUserDto = {
5450
+ export const $ActualBalanceDto = {
4037
5451
  type: 'object',
4038
5452
  properties: {
4039
- accessToken: {
5453
+ amount: {
4040
5454
  type: 'string',
4041
- description: 'Access token for user verification',
4042
- example: 'abc123xyz'
5455
+ description:
5456
+ 'Actual balance amount as a decimal string (preserves precision for tolerance inference).',
5457
+ example: '1234.56'
5458
+ },
5459
+ ccy: {
5460
+ type: 'string',
5461
+ description: 'Currency code (ISO 4217 or commodity ticker).',
5462
+ example: 'CNY'
4043
5463
  }
4044
5464
  },
4045
- required: ['accessToken']
5465
+ required: ['amount', 'ccy']
4046
5466
  } as const;
4047
5467
 
4048
- export const $SignupDto = {
5468
+ export const $ComputeReconciliationDto = {
4049
5469
  type: 'object',
4050
5470
  properties: {
4051
- turnstileToken: {
5471
+ accountId: {
4052
5472
  type: 'string',
4053
- description:
4054
- 'Cloudflare Turnstile verification token (optional when Turnstile disabled)',
4055
- example: '0.abc123def456...'
5473
+ description: 'BeanAccount id to reconcile.'
5474
+ },
5475
+ asOfDate: {
5476
+ type: 'string',
5477
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5478
+ example: '2026-07-24'
5479
+ },
5480
+ actualBalance: {
5481
+ description: 'Actual balance from the external statement.',
5482
+ allOf: [
5483
+ {
5484
+ $ref: '#/components/schemas/ActualBalanceDto'
5485
+ }
5486
+ ]
4056
5487
  }
4057
- }
5488
+ },
5489
+ required: ['accountId', 'asOfDate', 'actualBalance']
4058
5490
  } as const;
4059
5491
 
4060
- export const $UpdateUserSettingDto = {
5492
+ export const $ReconciliationComputeResultDto = {
4061
5493
  type: 'object',
4062
5494
  properties: {
4063
- secId: {
4064
- type: 'number',
4065
- description: 'Security ID'
5495
+ accountId: {
5496
+ type: 'string'
4066
5497
  },
4067
- annualInterestRate: {
4068
- type: 'number',
4069
- description: 'Annual interest rate',
4070
- example: 0.05
5498
+ asOfDate: {
5499
+ type: 'string'
4071
5500
  },
4072
- currency: {
5501
+ bookBalance: {
4073
5502
  type: 'string',
4074
- description: 'Currency code',
4075
- example: 'USD'
5503
+ description: 'System-computed book balance (decimal string).'
4076
5504
  },
4077
- baseCurrency: {
5505
+ actualBalance: {
4078
5506
  type: 'string',
4079
- description: 'Base currency code',
4080
- example: 'USD'
5507
+ description: 'User-entered actual balance (decimal string).'
4081
5508
  },
4082
- benchmark: {
4083
- type: 'string',
4084
- description: 'Benchmark symbol',
4085
- example: 'SPY'
5509
+ currency: {
5510
+ type: 'string'
4086
5511
  },
4087
- colorScheme: {
5512
+ diff: {
4088
5513
  type: 'string',
4089
- description: 'Color scheme',
4090
- enum: ['DARK', 'LIGHT']
5514
+ description: 'Diff = book − actual (decimal string).'
4091
5515
  },
4092
- dateRange: {
5516
+ tolerance: {
4093
5517
  type: 'string',
4094
- description: 'Date range filter',
4095
- example: '1y'
4096
- },
4097
- emergencyFund: {
4098
- type: 'number',
4099
- description: 'Emergency fund amount',
4100
- example: 10000
4101
- },
4102
- 'filters.accounts': {
4103
- description: 'Account filter IDs',
4104
- type: 'array',
4105
- items: {
4106
- type: 'string'
4107
- }
5518
+ description: 'Applied tolerance (decimal string).'
4108
5519
  },
4109
- 'filters.assetClasses': {
4110
- description: 'Asset class filters',
4111
- type: 'array',
4112
- items: {
4113
- type: 'string'
4114
- }
5520
+ withinTolerance: {
5521
+ type: 'boolean',
5522
+ description: 'true when |diff| ≤ tolerance.'
4115
5523
  },
4116
- 'filters.dataSource': {
5524
+ suggestedAction: {
4117
5525
  type: 'string',
4118
- description: 'Data source filter'
5526
+ enum: ['assert', 'pad'],
5527
+ description:
5528
+ 'Suggested next action: assert when within tolerance, pad otherwise.'
5529
+ }
5530
+ },
5531
+ required: [
5532
+ 'accountId',
5533
+ 'asOfDate',
5534
+ 'bookBalance',
5535
+ 'actualBalance',
5536
+ 'currency',
5537
+ 'diff',
5538
+ 'tolerance',
5539
+ 'withinTolerance',
5540
+ 'suggestedAction'
5541
+ ]
5542
+ } as const;
5543
+
5544
+ export const $AssertReconciliationDto = {
5545
+ type: 'object',
5546
+ properties: {
5547
+ accountId: {
5548
+ type: 'string',
5549
+ description: 'BeanAccount id to reconcile.'
4119
5550
  },
4120
- 'filters.symbol': {
5551
+ asOfDate: {
4121
5552
  type: 'string',
4122
- description: 'Symbol filter'
5553
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5554
+ example: '2026-07-24'
4123
5555
  },
4124
- 'filters.tags': {
4125
- description: 'Tag filters',
4126
- type: 'array',
4127
- items: {
4128
- type: 'string'
4129
- }
5556
+ actualBalance: {
5557
+ description: 'Actual balance from the external statement.',
5558
+ allOf: [
5559
+ {
5560
+ $ref: '#/components/schemas/ActualBalanceDto'
5561
+ }
5562
+ ]
4130
5563
  },
4131
- isExperimentalFeatures: {
4132
- type: 'boolean',
4133
- description: 'Enable experimental features'
5564
+ tolerance: {
5565
+ type: 'string',
5566
+ description:
5567
+ 'Optional explicit tolerance override. Omit to infer from amount precision (Beancount default).',
5568
+ example: '0.01'
5569
+ }
5570
+ },
5571
+ required: ['accountId', 'asOfDate', 'actualBalance']
5572
+ } as const;
5573
+
5574
+ export const $ReconciliationRecordDto = {
5575
+ type: 'object',
5576
+ properties: {
5577
+ id: {
5578
+ type: 'string'
4134
5579
  },
4135
- isRestrictedView: {
4136
- type: 'boolean',
4137
- description: 'Enable restricted view mode'
5580
+ accountId: {
5581
+ type: 'string'
4138
5582
  },
4139
- language: {
5583
+ date: {
5584
+ type: 'string'
5585
+ },
5586
+ amount: {
4140
5587
  type: 'string',
4141
- description: 'Language code',
4142
- example: 'en'
5588
+ description: 'Asserted (actual) amount.'
4143
5589
  },
4144
- locale: {
5590
+ currency: {
5591
+ type: 'string'
5592
+ },
5593
+ tolerance: {
5594
+ type: 'string'
5595
+ },
5596
+ diffAmount: {
4145
5597
  type: 'string',
4146
- description: 'Locale code',
4147
- example: 'en-US'
5598
+ description: 'book − actual.'
4148
5599
  },
4149
- projectedTotalAmount: {
4150
- type: 'number',
4151
- description: 'Projected total amount',
4152
- example: 1000000
5600
+ diffCurrency: {
5601
+ type: 'string'
4153
5602
  },
4154
- retirementDate: {
5603
+ createdAt: {
5604
+ type: 'string'
5605
+ }
5606
+ },
5607
+ required: ['id', 'accountId', 'date', 'amount', 'currency', 'createdAt']
5608
+ } as const;
5609
+
5610
+ export const $PadReconciliationDto = {
5611
+ type: 'object',
5612
+ properties: {
5613
+ accountId: {
4155
5614
  type: 'string',
4156
- description: 'Retirement date in ISO 8601 format',
4157
- example: '2050-01-01'
5615
+ description: 'BeanAccount id to reconcile.'
4158
5616
  },
4159
- savingsRate: {
4160
- type: 'number',
4161
- description: 'Savings rate percentage',
4162
- example: 0.2
5617
+ asOfDate: {
5618
+ type: 'string',
5619
+ description: 'Assertion date (ISO 8601, e.g. "2026-07-24").',
5620
+ example: '2026-07-24'
5621
+ },
5622
+ actualBalance: {
5623
+ description: 'Actual balance from the external statement.',
5624
+ allOf: [
5625
+ {
5626
+ $ref: '#/components/schemas/ActualBalanceDto'
5627
+ }
5628
+ ]
4163
5629
  },
4164
- viewMode: {
5630
+ sourceAccount: {
4165
5631
  type: 'string',
4166
- description: 'View mode',
4167
- enum: ['DEFAULT', 'ZEN']
5632
+ description:
5633
+ 'Pad source account. Defaults to Equity:Opening-Balances (official Beancount convention).',
5634
+ example: 'Equity:Opening-Balances',
5635
+ default: 'Equity:Opening-Balances'
4168
5636
  }
4169
- }
5637
+ },
5638
+ required: ['accountId', 'asOfDate', 'actualBalance']
4170
5639
  } as const;
4171
5640
 
4172
- export const $UpdatePropertyDto = {
5641
+ export const $PadResultDto = {
4173
5642
  type: 'object',
4174
5643
  properties: {
4175
- value: {
5644
+ transactionId: {
4176
5645
  type: 'string',
4177
- description: 'Property value'
5646
+ description: 'Created pad adjusting transaction id.'
4178
5647
  }
4179
5648
  },
4180
- required: ['value']
5649
+ required: ['transactionId']
4181
5650
  } as const;
4182
5651
 
4183
5652
  export const $FileImportDto = {
@@ -4346,7 +5815,7 @@ export const $IdentifyResultDto = {
4346
5815
  account: {
4347
5816
  type: 'string',
4348
5817
  description: 'Default account used by this importer',
4349
- example: 'Assets:Alipay:Balance'
5818
+ example: 'Assets:CN:Alipay:Balance'
4350
5819
  },
4351
5820
  message: {
4352
5821
  type: 'string',
@@ -4363,7 +5832,7 @@ export const $MapperDefaultsDto = {
4363
5832
  sourceAccount: {
4364
5833
  type: 'string',
4365
5834
  description: 'Source account for transactions (Beancount format)',
4366
- example: 'Assets:Alipay:Balance'
5835
+ example: 'Assets:CN:Alipay:Balance'
4367
5836
  },
4368
5837
  currency: {
4369
5838
  type: 'string',
@@ -4400,7 +5869,7 @@ export const $MapperDefaultsDto = {
4400
5869
  description:
4401
5870
  '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).',
4402
5871
  example: {
4403
- HuaBei: 'Liabilities:Alipay:Huabei',
5872
+ HuaBei: 'Liabilities:CN:CreditLine',
4404
5873
  CreditCard: 'Liabilities:CreditCard'
4405
5874
  }
4406
5875
  }
@@ -4532,8 +6001,9 @@ export const $UpdateMapperDefaultsDto = {
4532
6001
  sourceAccount: {
4533
6002
  type: 'string',
4534
6003
  description: 'Source account for transactions (Beancount format)',
4535
- example: 'Assets:Alipay:Balance',
4536
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6004
+ example: 'Assets:CN:Alipay:Balance',
6005
+ pattern:
6006
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4537
6007
  },
4538
6008
  currency: {
4539
6009
  type: 'string',
@@ -4547,20 +6017,22 @@ export const $UpdateMapperDefaultsDto = {
4547
6017
  type: 'string',
4548
6018
  description: 'Default expense account (optional)',
4549
6019
  example: 'Expenses:Unknown',
4550
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6020
+ pattern:
6021
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4551
6022
  },
4552
6023
  incomeAccount: {
4553
6024
  type: 'string',
4554
6025
  description: 'Default income account (optional)',
4555
6026
  example: 'Income:Unknown',
4556
- pattern: '^[A-Z][a-zA-Z0-9-]*:[a-zA-Z0-9-:]+$'
6027
+ pattern:
6028
+ '^(Assets|Liabilities|Income|Expenses|Equity)(:[A-Za-z0-9][A-Za-z0-9-]*)+$'
4557
6029
  },
4558
6030
  methodAccountMapping: {
4559
6031
  type: 'object',
4560
6032
  description:
4561
6033
  '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).',
4562
6034
  example: {
4563
- HuaBei: 'Liabilities:Alipay:Huabei',
6035
+ HuaBei: 'Liabilities:CN:CreditLine',
4564
6036
  CreditCard: 'Liabilities:CreditCard'
4565
6037
  }
4566
6038
  }
@@ -4595,119 +6067,13 @@ export const $UpdateImporterConfigDto = {
4595
6067
  }
4596
6068
  } as const;
4597
6069
 
4598
- export const $CreatePlatformDto = {
4599
- type: 'object',
4600
- properties: {
4601
- name: {
4602
- type: 'string',
4603
- description: 'Platform name',
4604
- example: 'Binance'
4605
- },
4606
- canonical: {
4607
- type: 'string',
4608
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4609
- example: 'binance'
4610
- },
4611
- aliases: {
4612
- description: 'Platform aliases (multi-language names for lookup)',
4613
- example: ['Binance', 'Binance Exchange', 'BNB'],
4614
- type: 'array',
4615
- items: {
4616
- type: 'string'
4617
- }
4618
- },
4619
- url: {
4620
- type: 'string',
4621
- description: 'Platform URL',
4622
- example: 'https://www.binance.com'
4623
- },
4624
- type: {
4625
- type: 'string',
4626
- description: 'Platform type',
4627
- enum: [
4628
- 'BANK',
4629
- 'BROKERAGE',
4630
- 'CRYPTO_EXCHANGE',
4631
- 'PAYMENT',
4632
- 'INVESTMENT',
4633
- 'INSURANCE',
4634
- 'OTHER'
4635
- ],
4636
- example: 'CRYPTO_EXCHANGE'
4637
- },
4638
- logoUrl: {
4639
- type: 'string',
4640
- description: 'Platform logo URL',
4641
- example: 'https://example.com/logos/binance.png'
4642
- },
4643
- isActive: {
4644
- type: 'boolean',
4645
- description: 'Whether the platform is active',
4646
- default: true
4647
- }
4648
- },
4649
- required: ['name', 'canonical', 'aliases', 'url', 'type']
4650
- } as const;
4651
-
4652
- export const $UpdatePlatformDto = {
4653
- type: 'object',
4654
- properties: {
4655
- name: {
4656
- type: 'string',
4657
- description: 'Platform name',
4658
- example: 'Binance'
4659
- },
4660
- canonical: {
4661
- type: 'string',
4662
- description: 'Platform canonical identifier (lowercase, kebab-case)',
4663
- example: 'binance'
4664
- },
4665
- aliases: {
4666
- description: 'Platform aliases (multi-language names for lookup)',
4667
- example: ['Binance', 'Binance Exchange', 'BNB'],
4668
- type: 'array',
4669
- items: {
4670
- type: 'string'
4671
- }
4672
- },
4673
- url: {
4674
- type: 'string',
4675
- description: 'Platform URL',
4676
- example: 'https://www.binance.com'
4677
- },
4678
- type: {
4679
- type: 'string',
4680
- description: 'Platform type',
4681
- enum: [
4682
- 'BANK',
4683
- 'BROKERAGE',
4684
- 'CRYPTO_EXCHANGE',
4685
- 'PAYMENT',
4686
- 'INVESTMENT',
4687
- 'INSURANCE',
4688
- 'OTHER'
4689
- ],
4690
- example: 'CRYPTO_EXCHANGE'
4691
- },
4692
- logoUrl: {
4693
- type: 'string',
4694
- description: 'Platform logo URL',
4695
- example: 'https://example.com/logos/binance.png'
4696
- },
4697
- isActive: {
4698
- type: 'boolean',
4699
- description: 'Whether the platform is active'
4700
- }
4701
- }
4702
- } as const;
4703
-
4704
6070
  export const $ProviderSyncConfigDto = {
4705
6071
  type: 'object',
4706
6072
  properties: {
4707
6073
  sourceAccount: {
4708
6074
  type: 'string',
4709
6075
  description: 'Source account for the first posting',
4710
- example: 'Assets:Bank:Chase'
6076
+ example: 'Assets:US:Chase:Checking'
4711
6077
  },
4712
6078
  defaultCurrency: {
4713
6079
  type: 'string',
@@ -4716,26 +6082,29 @@ export const $ProviderSyncConfigDto = {
4716
6082
  },
4717
6083
  defaultExpenseAccount: {
4718
6084
  type: 'string',
4719
- description: 'Default expense account for the second posting',
6085
+ description:
6086
+ 'Default expense account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
4720
6087
  example: 'Expenses:Unknown'
4721
6088
  },
4722
6089
  defaultIncomeAccount: {
4723
6090
  type: 'string',
4724
- description: 'Default income account for the second posting',
6091
+ description:
6092
+ 'Default income account for the second posting. Omit when no real default exists; the pipeline routes to Review via the Uncategorized sentinel (#618).',
4725
6093
  example: 'Income:Unknown'
4726
6094
  },
4727
6095
  filterPending: {
4728
6096
  type: 'boolean',
4729
6097
  description: 'Filter pending transactions',
4730
6098
  default: true
6099
+ },
6100
+ externalAccountId: {
6101
+ type: 'string',
6102
+ description:
6103
+ 'External account ID for per-batch providers (e.g. GoCardless). Overrides sourceAccount when an ExternalAccountLink mapping exists.',
6104
+ example: 'acc_gocardless_001'
4731
6105
  }
4732
6106
  },
4733
- required: [
4734
- 'sourceAccount',
4735
- 'defaultCurrency',
4736
- 'defaultExpenseAccount',
4737
- 'defaultIncomeAccount'
4738
- ]
6107
+ required: ['sourceAccount', 'defaultCurrency']
4739
6108
  } as const;
4740
6109
 
4741
6110
  export const $ProviderSyncDto = {
@@ -4843,18 +6212,189 @@ export const $SupportedProvidersResponseDto = {
4843
6212
  type: 'string'
4844
6213
  }
4845
6214
  }
4846
- },
4847
- required: ['providers']
4848
- } as const;
4849
-
4850
- export const $ParserTelemetryReportDto = {
4851
- type: 'object',
4852
- properties: {}
4853
- } as const;
4854
-
4855
- export const $UncoveredFormatMissDto = {
4856
- type: 'object',
4857
- properties: {}
6215
+ },
6216
+ required: ['providers']
6217
+ } as const;
6218
+
6219
+ export const $CreateExternalAccountLinkDto = {
6220
+ type: 'object',
6221
+ properties: {
6222
+ provider: {
6223
+ type: 'string',
6224
+ enum: [
6225
+ 'plaid',
6226
+ 'teller',
6227
+ 'truelayer',
6228
+ 'gocardless',
6229
+ 'simplefin',
6230
+ 'yodlee',
6231
+ 'beancount-direct',
6232
+ 'parsed-bill'
6233
+ ],
6234
+ example: 'plaid',
6235
+ description: 'Open Banking provider (whitelist)'
6236
+ },
6237
+ externalAccountId: {
6238
+ type: 'string',
6239
+ example: 'acc-plaid-001',
6240
+ description: 'External account ID from the provider'
6241
+ },
6242
+ beanAccountId: {
6243
+ type: 'string',
6244
+ example: '550e8400-e29b-41d4-a716-446655440000',
6245
+ description: 'Target BeanAccount ID (must belong to the JWT user)'
6246
+ }
6247
+ },
6248
+ required: ['provider', 'externalAccountId', 'beanAccountId']
6249
+ } as const;
6250
+
6251
+ export const $ExternalAccountLinkResponseDto = {
6252
+ type: 'object',
6253
+ properties: {
6254
+ id: {
6255
+ type: 'string'
6256
+ },
6257
+ provider: {
6258
+ type: 'string'
6259
+ },
6260
+ externalAccountId: {
6261
+ type: 'string'
6262
+ },
6263
+ beanAccountId: {
6264
+ type: 'string'
6265
+ },
6266
+ isActive: {
6267
+ type: 'boolean'
6268
+ },
6269
+ createdAt: {
6270
+ type: 'string'
6271
+ },
6272
+ updatedAt: {
6273
+ type: 'string'
6274
+ }
6275
+ },
6276
+ required: [
6277
+ 'id',
6278
+ 'provider',
6279
+ 'externalAccountId',
6280
+ 'beanAccountId',
6281
+ 'isActive',
6282
+ 'createdAt',
6283
+ 'updatedAt'
6284
+ ]
6285
+ } as const;
6286
+
6287
+ export const $ExternalAccountLinkListResponseDto = {
6288
+ type: 'object',
6289
+ properties: {
6290
+ items: {
6291
+ type: 'array',
6292
+ items: {
6293
+ $ref: '#/components/schemas/ExternalAccountLinkResponseDto'
6294
+ }
6295
+ },
6296
+ total: {
6297
+ type: 'number'
6298
+ },
6299
+ provider: {
6300
+ type: 'string',
6301
+ description: 'Filter by provider (query param)'
6302
+ }
6303
+ },
6304
+ required: ['items', 'total']
6305
+ } as const;
6306
+
6307
+ export const $ParserTelemetryReportDto = {
6308
+ type: 'object',
6309
+ properties: {}
6310
+ } as const;
6311
+
6312
+ export const $UncoveredFormatMissDto = {
6313
+ type: 'object',
6314
+ properties: {}
6315
+ } as const;
6316
+
6317
+ export const $ClientParsedDataDto = {
6318
+ type: 'object',
6319
+ properties: {
6320
+ amount: {
6321
+ type: 'number',
6322
+ description: 'Transaction amount',
6323
+ example: 35
6324
+ },
6325
+ currency: {
6326
+ type: 'string',
6327
+ description: 'Currency code',
6328
+ example: 'CNY'
6329
+ },
6330
+ date: {
6331
+ type: 'string',
6332
+ description: 'Transaction date (ISO 8601)',
6333
+ example: '2026-08-15'
6334
+ },
6335
+ payee: {
6336
+ type: 'string',
6337
+ description: 'Payee/merchant name',
6338
+ example: 'Starbucks'
6339
+ },
6340
+ narration: {
6341
+ type: 'string',
6342
+ description: 'Transaction narration'
6343
+ },
6344
+ category: {
6345
+ type: 'string',
6346
+ description: 'Category slug',
6347
+ example: 'food_restaurant'
6348
+ },
6349
+ incomeType: {
6350
+ type: 'string',
6351
+ description: 'Income type',
6352
+ example: 'Salary'
6353
+ },
6354
+ incomeSource: {
6355
+ type: 'string',
6356
+ description: 'Income source',
6357
+ example: 'Anthropic Inc.'
6358
+ },
6359
+ symbol: {
6360
+ type: 'string',
6361
+ description: 'Security symbol code (e.g., 600519, AAPL)',
6362
+ example: 'AAPL'
6363
+ },
6364
+ quantity: {
6365
+ type: 'number',
6366
+ description: 'Quantity of shares/units',
6367
+ example: 100
6368
+ },
6369
+ price: {
6370
+ type: 'number',
6371
+ description: 'Unit price per share/unit',
6372
+ example: 1900
6373
+ },
6374
+ investmentAction: {
6375
+ type: 'string',
6376
+ description: 'Investment action',
6377
+ enum: ['buy', 'sell'],
6378
+ example: 'buy'
6379
+ },
6380
+ paymentSource: {
6381
+ type: 'string',
6382
+ description: 'Payment source: asset (default) or liability (credit card)',
6383
+ enum: ['asset', 'liability'],
6384
+ example: 'asset'
6385
+ },
6386
+ liabilityHint: {
6387
+ type: 'string',
6388
+ description: 'Liability account hint (CreditCard/Huabei/Baitiao)',
6389
+ example: 'CreditCard'
6390
+ },
6391
+ warning: {
6392
+ type: 'string',
6393
+ description:
6394
+ 'Display-only warning from the prior response; accepted but ignored.',
6395
+ example: 'Cross-currency settlement applies.'
6396
+ }
6397
+ }
4858
6398
  } as const;
4859
6399
 
4860
6400
  export const $ProcessNlpDto = {
@@ -4862,10 +6402,17 @@ export const $ProcessNlpDto = {
4862
6402
  properties: {
4863
6403
  message: {
4864
6404
  type: 'string',
4865
- description: 'Natural language text describing a transaction (Chinese)',
4866
- example: 'yesterday Starbucks spent 35 yuan',
6405
+ description:
6406
+ 'Natural language text describing a transaction. Optional when `confirm` is true (structured confirm); otherwise required.',
6407
+ example: 'Starbucks 35',
4867
6408
  maxLength: 500
4868
6409
  },
6410
+ confirm: {
6411
+ type: 'boolean',
6412
+ description:
6413
+ 'Structured confirm signal — bypasses NL confirm-word matching when true. Send parsedData field edits alongside. The NL word-list path is the fallback.',
6414
+ example: true
6415
+ },
4869
6416
  sessionId: {
4870
6417
  type: 'string',
4871
6418
  description:
@@ -4873,17 +6420,51 @@ export const $ProcessNlpDto = {
4873
6420
  example: 'session_abc123'
4874
6421
  },
4875
6422
  parsedData: {
4876
- type: 'object',
4877
6423
  description:
4878
6424
  'Parsed data from previous NLP response for session recovery. Send back the parsedData received in confirm_payee/confirm responses.',
4879
6425
  example: {
4880
6426
  amount: 35,
4881
6427
  currency: 'CNY',
4882
6428
  payee: 'Starbucks'
4883
- }
6429
+ },
6430
+ allOf: [
6431
+ {
6432
+ $ref: '#/components/schemas/ClientParsedDataDto'
6433
+ }
6434
+ ]
6435
+ },
6436
+ selectedRuleId: {
6437
+ type: 'string',
6438
+ description:
6439
+ '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.',
6440
+ example: 'rule_abc123'
6441
+ },
6442
+ selectedAccount: {
6443
+ type: 'string',
6444
+ description:
6445
+ 'confirm_account echo-back: account path selected from the prior confirm_account response (suggestedAccount, similarAccounts[i].path, or a typed path). Applied directly when the session is confirming_account — no NL re-parse.',
6446
+ example: 'Expenses:Food:Coffee'
6447
+ },
6448
+ viewpointAccount: {
6449
+ type: 'string',
6450
+ description:
6451
+ 'Viewpoint account hint: the beancount path the user drilled into (e.g. from an account drill-down). Tie-break only — never overrides accounts resolved from the text. Must be an owned, OPEN Assets:/Liabilities: account; unresolvable hints are silently ignored.',
6452
+ example: 'Assets:CN:Bank:ICBC'
6453
+ },
6454
+ viewpointCategory: {
6455
+ type: 'string',
6456
+ description:
6457
+ "Viewpoint category hint: the ADR-0075 Group segment the user drilled into (e.g. 'Food'). Resolved to a concrete OPEN account in that group; tie-break only — never overrides a category resolved from the text.",
6458
+ example: 'Food'
6459
+ },
6460
+ viewpointFlow: {
6461
+ type: 'string',
6462
+ description:
6463
+ "Companion flow root for viewpointCategory ('income' | 'expense'), mirroring the ADR-0126 list-endpoint invariant. Derived from the session's routed intent (multi-turn) when absent; a first-turn flow-less category hint is dropped — send the flow explicitly.",
6464
+ enum: ['income', 'expense'],
6465
+ example: 'expense'
4884
6466
  }
4885
- },
4886
- required: ['message']
6467
+ }
4887
6468
  } as const;
4888
6469
 
4889
6470
  export const $NlpTransactionInfoDto = {
@@ -5195,6 +6776,24 @@ export const $NlpRuleConfirmationDataDto = {
5195
6776
  ]
5196
6777
  } as const;
5197
6778
 
6779
+ export const $NlpAccountCandidateDto = {
6780
+ type: 'object',
6781
+ properties: {
6782
+ path: {
6783
+ type: 'string',
6784
+ description: 'Canonical beancount account path (echo back on selection)',
6785
+ example: 'Expenses:Food:Dining'
6786
+ },
6787
+ name: {
6788
+ type: 'string',
6789
+ description:
6790
+ 'Localized display name (ADR-0114 read-time projection, user locale)',
6791
+ example: '餐饮'
6792
+ }
6793
+ },
6794
+ required: ['path', 'name']
6795
+ } as const;
6796
+
5198
6797
  export const $NlpAccountConfirmationDataDto = {
5199
6798
  type: 'object',
5200
6799
  properties: {
@@ -5205,14 +6804,16 @@ export const $NlpAccountConfirmationDataDto = {
5205
6804
  },
5206
6805
  suggestedAccount: {
5207
6806
  type: 'string',
5208
- description: 'Suggested replacement account',
6807
+ description:
6808
+ 'Suggested replacement account (omitted when no clear candidate)',
5209
6809
  example: 'Expenses:Food:Drinks'
5210
6810
  },
5211
6811
  similarAccounts: {
5212
- description: 'Similar accounts for user selection',
6812
+ description:
6813
+ 'Similar accounts for user selection (path + localized name, #680)',
5213
6814
  type: 'array',
5214
6815
  items: {
5215
- type: 'string'
6816
+ $ref: '#/components/schemas/NlpAccountCandidateDto'
5216
6817
  }
5217
6818
  },
5218
6819
  errorMessage: {
@@ -5227,7 +6828,6 @@ export const $NlpAccountConfirmationDataDto = {
5227
6828
  },
5228
6829
  required: [
5229
6830
  'invalidAccount',
5230
- 'suggestedAccount',
5231
6831
  'similarAccounts',
5232
6832
  'errorMessage',
5233
6833
  'transactionContext'
@@ -5396,11 +6996,12 @@ export const $NlpSuggestedAccountDto = {
5396
6996
  account: {
5397
6997
  type: 'string',
5398
6998
  description: 'Suggested account path',
5399
- example: 'Assets:Bank:Checking'
6999
+ example: 'Assets:Checking'
5400
7000
  },
5401
7001
  confidence: {
5402
7002
  type: 'number',
5403
- description: 'Confidence score for this suggestion (0-1)',
7003
+ description:
7004
+ 'Confidence score for this suggestion (0-1). Present = predicted (confirm/confirm_rule/confirm_account); omitted = actual persisted account (created). (#586)',
5404
7005
  example: 0.9
5405
7006
  }
5406
7007
  },
@@ -5436,23 +7037,31 @@ export const $NlpDefaultAccountsDto = {
5436
7037
  properties: {
5437
7038
  asset: {
5438
7039
  type: 'string',
5439
- description: 'Default asset account',
5440
- example: 'Assets:Bank:Checking'
7040
+ description:
7041
+ 'Default OPEN asset account (MRU when multiple), or null when none/ambiguous',
7042
+ example: 'Assets:Checking',
7043
+ nullable: true
5441
7044
  },
5442
7045
  expense: {
5443
7046
  type: 'string',
5444
- description: 'Default expense account',
5445
- example: 'Expenses:Uncategorized'
7047
+ description:
7048
+ 'Default OPEN expense account (MRU when multiple), or null when none/ambiguous',
7049
+ example: 'Expenses:Food:Coffee',
7050
+ nullable: true
5446
7051
  },
5447
7052
  income: {
5448
7053
  type: 'string',
5449
- description: 'Default income account',
5450
- example: 'Income:Uncategorized'
7054
+ description:
7055
+ 'Default OPEN income account (MRU when multiple), or null when none/ambiguous',
7056
+ example: 'Income:Salary',
7057
+ nullable: true
5451
7058
  },
5452
7059
  liability: {
5453
7060
  type: 'string',
5454
- description: 'Default liability account',
5455
- example: 'Liabilities:CreditCard'
7061
+ description:
7062
+ 'Default OPEN liability account (MRU when multiple), or null when none/ambiguous',
7063
+ example: 'Liabilities:CreditCard',
7064
+ nullable: true
5456
7065
  }
5457
7066
  },
5458
7067
  required: ['asset', 'expense', 'income', 'liability']
@@ -5477,7 +7086,8 @@ export const $NlpResponseDto = {
5477
7086
  'confirm_rule',
5478
7087
  'confirm_account',
5479
7088
  'confirm_payee',
5480
- 'cancel'
7089
+ 'cancel',
7090
+ 'aborted'
5481
7091
  ]
5482
7092
  },
5483
7093
  intent: {
@@ -5491,7 +7101,7 @@ export const $NlpResponseDto = {
5491
7101
  type: 'string',
5492
7102
  description:
5493
7103
  'Asset sub-type (only present when intent is "asset"). Determines which asset-related form to render.',
5494
- enum: ['transfer', 'banking', 'investment'],
7104
+ enum: ['transfer', 'banking', 'investment', 'lend', 'lend_collect'],
5495
7105
  example: 'investment'
5496
7106
  },
5497
7107
  liabilitySubType: {
@@ -5608,72 +7218,407 @@ export const $NlpResponseDto = {
5608
7218
  }
5609
7219
  ]
5610
7220
  },
5611
- confidence: {
7221
+ confidence: {
7222
+ type: 'number',
7223
+ description: 'Overall confidence score (0-1)',
7224
+ example: 0.85
7225
+ },
7226
+ confidenceThreshold: {
7227
+ type: 'number',
7228
+ description:
7229
+ 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
7230
+ example: 0.75
7231
+ },
7232
+ recurringMatch: {
7233
+ description:
7234
+ 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
7235
+ allOf: [
7236
+ {
7237
+ $ref: '#/components/schemas/RecurringMatchInfoDto'
7238
+ }
7239
+ ]
7240
+ },
7241
+ recurringSuggestion: {
7242
+ description:
7243
+ 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
7244
+ allOf: [
7245
+ {
7246
+ $ref: '#/components/schemas/RecurringSuggestionDto'
7247
+ }
7248
+ ]
7249
+ },
7250
+ suggestedAccounts: {
7251
+ description:
7252
+ 'Suggested accounts for this transaction (#586). confirm/confirm_rule/confirm_account: predicted (source/destination carry confidence); created: actual persisted accounts (confidence omitted). confirm_account destination is the suggested replacement, never the invalid account.',
7253
+ allOf: [
7254
+ {
7255
+ $ref: '#/components/schemas/NlpSuggestedAccountsDto'
7256
+ }
7257
+ ]
7258
+ },
7259
+ defaultAccounts: {
7260
+ description:
7261
+ 'Default fallback accounts for the user/region (#586). v1 returns universal constants; per-user personalization is planned.',
7262
+ allOf: [
7263
+ {
7264
+ $ref: '#/components/schemas/NlpDefaultAccountsDto'
7265
+ }
7266
+ ]
7267
+ }
7268
+ },
7269
+ required: ['status', 'action']
7270
+ } as const;
7271
+
7272
+ export const $PlatformListItemDto = {
7273
+ type: 'object',
7274
+ properties: {
7275
+ id: {
7276
+ type: 'string',
7277
+ description: 'Global platform ID'
7278
+ },
7279
+ name: {
7280
+ type: 'string',
7281
+ description: 'Platform name'
7282
+ },
7283
+ url: {
7284
+ type: 'string',
7285
+ description: 'Platform URL'
7286
+ },
7287
+ type: {
7288
+ type: 'string',
7289
+ description: 'Platform type',
7290
+ enum: [
7291
+ 'BANK',
7292
+ 'BROKERAGE',
7293
+ 'CRYPTO_EXCHANGE',
7294
+ 'PAYMENT',
7295
+ 'INVESTMENT',
7296
+ 'INSURANCE',
7297
+ 'OTHER'
7298
+ ]
7299
+ },
7300
+ canonical: {
7301
+ type: 'string',
7302
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7303
+ },
7304
+ suggestedSegment: {
7305
+ type: 'string',
7306
+ description:
7307
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7308
+ },
7309
+ logoUrl: {
7310
+ type: 'string',
7311
+ description: 'Logo URL',
7312
+ nullable: true
7313
+ },
7314
+ countryCode: {
7315
+ type: 'string',
7316
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7317
+ example: 'CN',
7318
+ nullable: true
7319
+ },
7320
+ category: {
7321
+ type: 'string',
7322
+ description:
7323
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7324
+ nullable: true,
7325
+ example: 'DigitalWallet'
7326
+ },
7327
+ isBound: {
7328
+ type: 'boolean',
7329
+ description: 'Whether user has accounts using this platform'
7330
+ }
7331
+ },
7332
+ required: [
7333
+ 'id',
7334
+ 'name',
7335
+ 'url',
7336
+ 'type',
7337
+ 'canonical',
7338
+ 'suggestedSegment',
7339
+ 'logoUrl',
7340
+ 'countryCode',
7341
+ 'category',
7342
+ 'isBound'
7343
+ ]
7344
+ } as const;
7345
+
7346
+ export const $PlatformMatchResultDto = {
7347
+ type: 'object',
7348
+ properties: {
7349
+ id: {
7350
+ type: 'string',
7351
+ description: 'Global platform ID'
7352
+ },
7353
+ name: {
7354
+ type: 'string',
7355
+ description: 'Platform name (e.g., "ICBC")'
7356
+ },
7357
+ canonical: {
7358
+ type: 'string',
7359
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7360
+ },
7361
+ type: {
7362
+ type: 'string',
7363
+ description: 'Platform type',
7364
+ enum: [
7365
+ 'BANK',
7366
+ 'BROKERAGE',
7367
+ 'CRYPTO_EXCHANGE',
7368
+ 'PAYMENT',
7369
+ 'INVESTMENT',
7370
+ 'INSURANCE',
7371
+ 'OTHER'
7372
+ ]
7373
+ },
7374
+ suggestedSegment: {
7375
+ type: 'string',
7376
+ description:
7377
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7378
+ },
7379
+ logoUrl: {
7380
+ type: 'string',
7381
+ description: 'Logo URL',
7382
+ nullable: true
7383
+ },
7384
+ countryCode: {
7385
+ type: 'string',
7386
+ description: 'ISO 3166-1 alpha-2 (UPPERCASE); null = global platform',
7387
+ example: 'CN',
7388
+ nullable: true
7389
+ },
7390
+ category: {
7391
+ type: 'string',
7392
+ description:
7393
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank). null = no region-aware suggestion; fall back to type.',
7394
+ nullable: true,
7395
+ example: 'DigitalWallet'
7396
+ },
7397
+ matchType: {
7398
+ type: 'string',
7399
+ description: "How this row matched: 'exact' > 'prefix' > 'substring'",
7400
+ enum: ['exact', 'prefix', 'substring']
7401
+ }
7402
+ },
7403
+ required: [
7404
+ 'id',
7405
+ 'name',
7406
+ 'canonical',
7407
+ 'type',
7408
+ 'suggestedSegment',
7409
+ 'logoUrl',
7410
+ 'countryCode',
7411
+ 'category',
7412
+ 'matchType'
7413
+ ]
7414
+ } as const;
7415
+
7416
+ export const $PlatformMatchResponseDto = {
7417
+ type: 'object',
7418
+ properties: {
7419
+ platforms: {
7420
+ description: 'Ranked matches, best tier first (at most 10 rows)',
7421
+ type: 'array',
7422
+ items: {
7423
+ $ref: '#/components/schemas/PlatformMatchResultDto'
7424
+ }
7425
+ },
7426
+ matchType: {
7427
+ type: 'string',
7428
+ description:
7429
+ "Overall match quality — top row's tier, or 'none' when no hits",
7430
+ enum: ['none', 'exact', 'prefix', 'substring']
7431
+ },
7432
+ total: {
5612
7433
  type: 'number',
5613
- description: 'Overall confidence score (0-1)',
5614
- example: 0.85
7434
+ description: 'Total matches before LIMIT (truncation transparency)'
5615
7435
  },
5616
- confidenceThreshold: {
5617
- type: 'number',
5618
- description:
5619
- 'Confidence threshold for automatic creation (default: 0.75). When confidence < threshold, action will be "confirm" requiring user verification.',
5620
- example: 0.75
7436
+ hasMore: {
7437
+ type: 'boolean',
7438
+ description: 'true when total > platforms.length (more matches exist)'
7439
+ }
7440
+ },
7441
+ required: ['platforms', 'matchType', 'total', 'hasMore']
7442
+ } as const;
7443
+
7444
+ export const $PlatformStandardsPlatformDto = {
7445
+ type: 'object',
7446
+ properties: {
7447
+ id: {
7448
+ type: 'string',
7449
+ description: 'Global platform ID'
5621
7450
  },
5622
- recurringMatch: {
7451
+ name: {
7452
+ type: 'string',
7453
+ description: 'Platform name (e.g., "ICBC")'
7454
+ },
7455
+ canonical: {
7456
+ type: 'string',
7457
+ description: 'Canonical identifier in ACCOUNT_RE format (e.g., "icbc")'
7458
+ },
7459
+ suggestedSegment: {
7460
+ type: 'string',
5623
7461
  description:
5624
- 'Recurring transaction match info (when action is "created"). Contains match details when transaction matches a pending expected transaction.',
5625
- allOf: [
5626
- {
5627
- $ref: '#/components/schemas/RecurringMatchInfoDto'
5628
- }
7462
+ 'Suggested path segment — canonical PascalCased per hyphen-part, hyphens preserved (e.g. "Apple-Pay")'
7463
+ },
7464
+ type: {
7465
+ type: 'string',
7466
+ description: 'Platform type',
7467
+ enum: [
7468
+ 'BANK',
7469
+ 'BROKERAGE',
7470
+ 'CRYPTO_EXCHANGE',
7471
+ 'PAYMENT',
7472
+ 'INVESTMENT',
7473
+ 'INSURANCE',
7474
+ 'OTHER'
5629
7475
  ]
5630
7476
  },
5631
- recurringSuggestion: {
7477
+ category: {
7478
+ type: 'string',
5632
7479
  description:
5633
- 'Recurring rule creation suggestion (when action is "created"). Contains suggestion to create a recurring rule based on detected patterns. Only present when no existing rule matched and similar historical transactions were found.',
7480
+ 'Region-aware category (institution vocab, e.g. DigitalWallet/Bank) resolved against the final region. null = no region-aware suggestion; fall back to type.',
7481
+ nullable: true,
7482
+ example: 'Bank'
7483
+ }
7484
+ },
7485
+ required: ['id', 'name', 'canonical', 'suggestedSegment', 'type', 'category']
7486
+ } as const;
7487
+
7488
+ export const $PlatformStandardsResponseDto = {
7489
+ type: 'object',
7490
+ properties: {
7491
+ platform: {
7492
+ description: 'The selected platform (institution lock source)',
5634
7493
  allOf: [
5635
7494
  {
5636
- $ref: '#/components/schemas/RecurringSuggestionDto'
7495
+ $ref: '#/components/schemas/PlatformStandardsPlatformDto'
5637
7496
  }
5638
7497
  ]
5639
7498
  },
5640
- suggestedAccounts: {
7499
+ region: {
7500
+ type: 'string',
5641
7501
  description:
5642
- 'Suggested accounts for this transaction. Contains recommended source and destination accounts based on the detected intent and rules.',
5643
- allOf: [
5644
- {
5645
- $ref: '#/components/schemas/NlpSuggestedAccountsDto'
5646
- }
5647
- ]
7502
+ "Resolved template region (ISO 3166-1 alpha-2, UPPERCASE): the platform's own countryCode when set, else the region query param. For regions without a regional template file the template list falls back to the universal-only catalog while region still echoes the code.",
7503
+ example: 'CN'
5648
7504
  },
5649
- defaultAccounts: {
7505
+ templates: {
5650
7506
  description:
5651
- 'Default accounts for the user/region. These are fallback accounts used when no specific suggestion is available.',
5652
- allOf: [
5653
- {
5654
- $ref: '#/components/schemas/NlpDefaultAccountsDto'
5655
- }
5656
- ]
7507
+ 'Candidate account-standard templates of the resolved region (groupable by productCategory client-side)',
7508
+ type: 'array',
7509
+ items: {
7510
+ $ref: '#/components/schemas/AccountStandardResponseDto'
7511
+ }
5657
7512
  }
5658
7513
  },
5659
- required: ['status', 'action']
7514
+ required: ['platform', 'region', 'templates']
5660
7515
  } as const;
5661
7516
 
5662
- export const $BalanceByCurrencyDto = {
7517
+ export const $CreatePlatformDto = {
5663
7518
  type: 'object',
5664
7519
  properties: {
5665
- currency: {
7520
+ name: {
5666
7521
  type: 'string',
5667
- description: 'ISO 4217 currency code',
5668
- example: 'CNY'
7522
+ description: 'Platform name',
7523
+ example: 'Binance'
5669
7524
  },
5670
- balance: {
7525
+ canonical: {
5671
7526
  type: 'string',
5672
- description: 'Balance amount',
5673
- example: '50000.00'
7527
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
7528
+ example: 'binance'
7529
+ },
7530
+ aliases: {
7531
+ description: 'Platform aliases (multi-language names for lookup)',
7532
+ example: ['Binance', 'Binance Exchange', 'BNB'],
7533
+ type: 'array',
7534
+ items: {
7535
+ type: 'string'
7536
+ }
7537
+ },
7538
+ url: {
7539
+ type: 'string',
7540
+ description: 'Platform URL',
7541
+ example: 'https://www.binance.com'
7542
+ },
7543
+ type: {
7544
+ type: 'string',
7545
+ description: 'Platform type',
7546
+ enum: [
7547
+ 'BANK',
7548
+ 'BROKERAGE',
7549
+ 'CRYPTO_EXCHANGE',
7550
+ 'PAYMENT',
7551
+ 'INVESTMENT',
7552
+ 'INSURANCE',
7553
+ 'OTHER'
7554
+ ],
7555
+ example: 'CRYPTO_EXCHANGE'
7556
+ },
7557
+ logoUrl: {
7558
+ type: 'string',
7559
+ description: 'Platform logo URL',
7560
+ example: 'https://example.com/logos/binance.png'
7561
+ },
7562
+ isActive: {
7563
+ type: 'boolean',
7564
+ description: 'Whether the platform is active',
7565
+ default: true
5674
7566
  }
5675
7567
  },
5676
- required: ['currency', 'balance']
7568
+ required: ['name', 'canonical', 'aliases', 'url', 'type']
7569
+ } as const;
7570
+
7571
+ export const $UpdatePlatformDto = {
7572
+ type: 'object',
7573
+ properties: {
7574
+ name: {
7575
+ type: 'string',
7576
+ description: 'Platform name',
7577
+ example: 'Binance'
7578
+ },
7579
+ canonical: {
7580
+ type: 'string',
7581
+ description: 'Platform canonical identifier (lowercase, kebab-case)',
7582
+ example: 'binance'
7583
+ },
7584
+ aliases: {
7585
+ description: 'Platform aliases (multi-language names for lookup)',
7586
+ example: ['Binance', 'Binance Exchange', 'BNB'],
7587
+ type: 'array',
7588
+ items: {
7589
+ type: 'string'
7590
+ }
7591
+ },
7592
+ url: {
7593
+ type: 'string',
7594
+ description: 'Platform URL',
7595
+ example: 'https://www.binance.com'
7596
+ },
7597
+ type: {
7598
+ type: 'string',
7599
+ description: 'Platform type',
7600
+ enum: [
7601
+ 'BANK',
7602
+ 'BROKERAGE',
7603
+ 'CRYPTO_EXCHANGE',
7604
+ 'PAYMENT',
7605
+ 'INVESTMENT',
7606
+ 'INSURANCE',
7607
+ 'OTHER'
7608
+ ],
7609
+ example: 'CRYPTO_EXCHANGE'
7610
+ },
7611
+ logoUrl: {
7612
+ type: 'string',
7613
+ description: 'Platform logo URL',
7614
+ example: 'https://example.com/logos/binance.png'
7615
+ },
7616
+ isActive: {
7617
+ type: 'boolean',
7618
+ description: 'Whether the platform is active',
7619
+ default: true
7620
+ }
7621
+ }
5677
7622
  } as const;
5678
7623
 
5679
7624
  export const $NetWorthByCurrencyDto = {
@@ -5745,28 +7690,6 @@ export const $ConvertedNetWorthDto = {
5745
7690
  ]
5746
7691
  } as const;
5747
7692
 
5748
- export const $ExchangeRateWarningDto = {
5749
- type: 'object',
5750
- properties: {
5751
- type: {
5752
- type: 'string',
5753
- description: 'Warning type',
5754
- example: 'MISSING_EXCHANGE_RATE'
5755
- },
5756
- currency: {
5757
- type: 'string',
5758
- description: 'Currency without exchange rate',
5759
- example: 'EUR'
5760
- },
5761
- totalAmount: {
5762
- type: 'string',
5763
- description: 'Total amount affected',
5764
- example: '1000.00'
5765
- }
5766
- },
5767
- required: ['type', 'currency', 'totalAmount']
5768
- } as const;
5769
-
5770
7693
  export const $NetWorthResponseDto = {
5771
7694
  type: 'object',
5772
7695
  properties: {
@@ -5852,11 +7775,12 @@ export const $AccountItemDto = {
5852
7775
  name: {
5853
7776
  type: 'string',
5854
7777
  description: 'Full account name',
5855
- example: 'Assets:Bank:CMB:Savings'
7778
+ example: 'Assets:CN:CMB:Savings'
5856
7779
  },
5857
7780
  displayName: {
5858
7781
  type: 'string',
5859
- description: 'Display name (last part of account path)',
7782
+ description:
7783
+ 'Display name: user-set name if provided (#762), else last part of account path',
5860
7784
  example: 'Savings'
5861
7785
  },
5862
7786
  balance: {
@@ -5868,6 +7792,12 @@ export const $AccountItemDto = {
5868
7792
  type: 'string',
5869
7793
  description: 'Currency code',
5870
7794
  example: 'CNY'
7795
+ },
7796
+ convertedBalance: {
7797
+ type: 'string',
7798
+ description:
7799
+ 'FX-converted balance in base currency; omitted when not convertible',
7800
+ example: '50000.00'
5871
7801
  }
5872
7802
  },
5873
7803
  required: ['id', 'name', 'displayName', 'balance', 'currency']
@@ -5878,27 +7808,84 @@ export const $PlatformGroupDto = {
5878
7808
  properties: {
5879
7809
  platformId: {
5880
7810
  type: 'string',
5881
- description: 'Platform ID'
7811
+ description: 'Platform ID'
7812
+ },
7813
+ platformName: {
7814
+ type: 'string',
7815
+ description: 'Platform display name',
7816
+ example: 'CMB Bank'
7817
+ },
7818
+ accounts: {
7819
+ description:
7820
+ 'Accounts within this platform (Assets and Liabilities rows, #696)',
7821
+ type: 'array',
7822
+ items: {
7823
+ $ref: '#/components/schemas/AccountItemDto'
7824
+ }
7825
+ },
7826
+ totalBalance: {
7827
+ type: 'string',
7828
+ description:
7829
+ 'FX-converted total balance in base currency (nets Assets + Liabilities rows; can be negative)',
7830
+ example: '100000.00'
7831
+ },
7832
+ balanceByCurrency: {
7833
+ description: 'Raw (unconverted) balances grouped by currency',
7834
+ type: 'array',
7835
+ items: {
7836
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
7837
+ }
7838
+ },
7839
+ convertedBalance: {
7840
+ type: 'string',
7841
+ description:
7842
+ 'Converted balance in base currency (omitted when no currency is convertible)',
7843
+ example: '100000.00'
7844
+ },
7845
+ sharePct: {
7846
+ type: 'number',
7847
+ description:
7848
+ 'Share of the converted asset-side grand total (0-100); liability balances are excluded from the basis; 0 when grand total is 0 (#696)',
7849
+ example: 42.5
7850
+ }
7851
+ },
7852
+ required: [
7853
+ 'platformId',
7854
+ 'platformName',
7855
+ 'accounts',
7856
+ 'totalBalance',
7857
+ 'balanceByCurrency',
7858
+ 'sharePct'
7859
+ ]
7860
+ } as const;
7861
+
7862
+ export const $AccountExchangeRateWarningDto = {
7863
+ type: 'object',
7864
+ properties: {
7865
+ type: {
7866
+ type: 'string',
7867
+ description: 'Warning type',
7868
+ example: 'MISSING_EXCHANGE_RATE'
5882
7869
  },
5883
- platformName: {
7870
+ currency: {
5884
7871
  type: 'string',
5885
- description: 'Platform display name',
5886
- example: 'CMB Bank'
7872
+ description: 'Currency without exchange rate',
7873
+ example: 'USD'
5887
7874
  },
5888
7875
  accounts: {
5889
- description: 'Accounts within this platform',
7876
+ description: 'Affected account paths',
5890
7877
  type: 'array',
5891
7878
  items: {
5892
- $ref: '#/components/schemas/AccountItemDto'
7879
+ type: 'string'
5893
7880
  }
5894
7881
  },
5895
- totalBalance: {
7882
+ totalAmount: {
5896
7883
  type: 'string',
5897
- description: 'Total balance across all accounts in platform',
5898
- example: '100000.00'
7884
+ description: 'Total amount in this currency',
7885
+ example: '5000.00'
5899
7886
  }
5900
7887
  },
5901
- required: ['platformId', 'platformName', 'accounts', 'totalBalance']
7888
+ required: ['type', 'currency', 'accounts', 'totalAmount']
5902
7889
  } as const;
5903
7890
 
5904
7891
  export const $AccountsSummaryDto = {
@@ -5906,14 +7893,27 @@ export const $AccountsSummaryDto = {
5906
7893
  properties: {
5907
7894
  totalAccounts: {
5908
7895
  type: 'number',
5909
- description: 'Total number of accounts'
7896
+ description:
7897
+ 'Total number of accounts (balance sheet: Assets + Liabilities, #696)'
5910
7898
  },
5911
7899
  totalPlatforms: {
5912
7900
  type: 'number',
5913
7901
  description: 'Total number of platforms'
7902
+ },
7903
+ baseCurrency: {
7904
+ type: 'string',
7905
+ description: 'Base currency for conversion',
7906
+ example: 'CNY'
7907
+ },
7908
+ warnings: {
7909
+ description: 'Per-account exchange rate warnings',
7910
+ type: 'array',
7911
+ items: {
7912
+ $ref: '#/components/schemas/AccountExchangeRateWarningDto'
7913
+ }
5914
7914
  }
5915
7915
  },
5916
- required: ['totalAccounts', 'totalPlatforms']
7916
+ required: ['totalAccounts', 'totalPlatforms', 'baseCurrency']
5917
7917
  } as const;
5918
7918
 
5919
7919
  export const $AccountsResponseDto = {
@@ -5948,11 +7948,12 @@ export const $AccountItemWithAssetClassDto = {
5948
7948
  name: {
5949
7949
  type: 'string',
5950
7950
  description: 'Full account name',
5951
- example: 'Assets:Bank:CMB:Savings'
7951
+ example: 'Assets:CN:CMB:Savings'
5952
7952
  },
5953
7953
  displayName: {
5954
7954
  type: 'string',
5955
- description: 'Display name (last part of account path)',
7955
+ description:
7956
+ 'Display name: user-set name if provided (#762), else last part of account path',
5956
7957
  example: 'Savings'
5957
7958
  },
5958
7959
  balance: {
@@ -5965,6 +7966,12 @@ export const $AccountItemWithAssetClassDto = {
5965
7966
  description: 'Currency code',
5966
7967
  example: 'CNY'
5967
7968
  },
7969
+ convertedBalance: {
7970
+ type: 'string',
7971
+ description:
7972
+ 'FX-converted balance in base currency; omitted when not convertible',
7973
+ example: '50000.00'
7974
+ },
5968
7975
  assetClass: {
5969
7976
  type: 'string',
5970
7977
  description: 'Asset class',
@@ -6044,35 +8051,6 @@ export const $AssetClassGroupDto = {
6044
8051
  required: ['assetClass', 'accounts', 'balanceByCurrency']
6045
8052
  } as const;
6046
8053
 
6047
- export const $AccountExchangeRateWarningDto = {
6048
- type: 'object',
6049
- properties: {
6050
- type: {
6051
- type: 'string',
6052
- description: 'Warning type',
6053
- example: 'MISSING_EXCHANGE_RATE'
6054
- },
6055
- currency: {
6056
- type: 'string',
6057
- description: 'Currency without exchange rate',
6058
- example: 'USD'
6059
- },
6060
- accounts: {
6061
- description: 'Affected account paths',
6062
- type: 'array',
6063
- items: {
6064
- type: 'string'
6065
- }
6066
- },
6067
- totalAmount: {
6068
- type: 'string',
6069
- description: 'Total amount in this currency',
6070
- example: '5000.00'
6071
- }
6072
- },
6073
- required: ['type', 'currency', 'accounts', 'totalAmount']
6074
- } as const;
6075
-
6076
8054
  export const $AssetClassSummaryDto = {
6077
8055
  type: 'object',
6078
8056
  properties: {
@@ -6146,7 +8124,7 @@ export const $HoldingAssetClassAccountSliceDto = {
6146
8124
  accountPath: {
6147
8125
  type: 'string',
6148
8126
  description: 'Full account path',
6149
- example: 'Assets:US:Investments:Brokerage'
8127
+ example: 'Assets:US:Fidelity:Brokerage'
6150
8128
  },
6151
8129
  accountCurrency: {
6152
8130
  type: 'string',
@@ -6352,6 +8330,101 @@ export const $CashFlowResponseDto = {
6352
8330
  ]
6353
8331
  } as const;
6354
8332
 
8333
+ export const $CategoryGroupDto = {
8334
+ type: 'object',
8335
+ properties: {
8336
+ category: {
8337
+ type: 'string',
8338
+ description:
8339
+ 'Functional category (account-path Group segment); regional and universal account paths merge under it',
8340
+ example: 'Food'
8341
+ },
8342
+ totalExpense: {
8343
+ type: 'string',
8344
+ description:
8345
+ 'Converted total for this category in base currency (expense amount when flow=expense, income amount when flow=income)',
8346
+ example: '1200.00'
8347
+ },
8348
+ sharePct: {
8349
+ type: 'number',
8350
+ description: 'Share of grand total (0-100); 0 when grand total is 0',
8351
+ example: 42.5
8352
+ },
8353
+ balanceByCurrency: {
8354
+ description: 'Raw (unconverted) expense per currency',
8355
+ type: 'array',
8356
+ items: {
8357
+ $ref: '#/components/schemas/BalanceByCurrencyDto'
8358
+ }
8359
+ },
8360
+ convertedBalance: {
8361
+ type: 'string',
8362
+ description:
8363
+ 'Converted total in base currency (omitted when FX missing for all currencies in this category)',
8364
+ example: '1200.00'
8365
+ }
8366
+ },
8367
+ required: ['category', 'totalExpense', 'sharePct', 'balanceByCurrency']
8368
+ } as const;
8369
+
8370
+ export const $ExpensesByCategorySummaryDto = {
8371
+ type: 'object',
8372
+ properties: {
8373
+ totalExpense: {
8374
+ type: 'string',
8375
+ description:
8376
+ 'Total across all categories, converted (convertible categories only); expense totals when flow=expense, income totals when flow=income',
8377
+ example: '5000.00'
8378
+ },
8379
+ categoryCount: {
8380
+ type: 'number',
8381
+ description: 'Number of categories',
8382
+ example: 8
8383
+ }
8384
+ },
8385
+ required: ['totalExpense', 'categoryCount']
8386
+ } as const;
8387
+
8388
+ export const $ExpensesByCategoryResponseDto = {
8389
+ type: 'object',
8390
+ properties: {
8391
+ period: {
8392
+ type: 'string',
8393
+ description: 'Period requested',
8394
+ example: '1m'
8395
+ },
8396
+ baseCurrency: {
8397
+ type: 'string',
8398
+ description: 'Base currency for converted values',
8399
+ example: 'CNY'
8400
+ },
8401
+ groups: {
8402
+ description:
8403
+ 'Expense groups by functional category, sorted by converted total desc',
8404
+ type: 'array',
8405
+ items: {
8406
+ $ref: '#/components/schemas/CategoryGroupDto'
8407
+ }
8408
+ },
8409
+ summary: {
8410
+ description: 'Summary statistics',
8411
+ allOf: [
8412
+ {
8413
+ $ref: '#/components/schemas/ExpensesByCategorySummaryDto'
8414
+ }
8415
+ ]
8416
+ },
8417
+ warnings: {
8418
+ description: 'Exchange rate warnings (e.g. missing rate for a currency)',
8419
+ type: 'array',
8420
+ items: {
8421
+ $ref: '#/components/schemas/ExchangeRateWarningDto'
8422
+ }
8423
+ }
8424
+ },
8425
+ required: ['period', 'baseCurrency', 'groups', 'summary']
8426
+ } as const;
8427
+
6355
8428
  export const $MonetaryDto = {
6356
8429
  type: 'object',
6357
8430
  properties: {
@@ -6366,7 +8439,7 @@ export const $MonetaryDto = {
6366
8439
  example: 'USD'
6367
8440
  },
6368
8441
  baseCcyEquivalent: {
6369
- type: 'object',
8442
+ type: 'string',
6370
8443
  description: 'Converted to user base currency (Decimal string)',
6371
8444
  example: '21600',
6372
8445
  nullable: true
@@ -6442,13 +8515,13 @@ export const $HoldingPnlRowDto = {
6442
8515
  example: 'Assets:US:Broker:AAPL'
6443
8516
  },
6444
8517
  accountCcy: {
6445
- type: 'object',
8518
+ type: 'string',
6446
8519
  description: 'Account settlement currency (ISO 4217), from cost currency',
6447
8520
  nullable: true,
6448
8521
  example: 'USD'
6449
8522
  },
6450
8523
  brokerType: {
6451
- type: 'object',
8524
+ type: 'string',
6452
8525
  description: 'Broker type derived from Platform.type',
6453
8526
  nullable: true,
6454
8527
  example: 'broker'
@@ -6469,7 +8542,7 @@ export const $HoldingPnlRowDto = {
6469
8542
  example: 'EQUITY'
6470
8543
  },
6471
8544
  assetSubClass: {
6472
- type: 'object',
8545
+ type: 'string',
6473
8546
  nullable: true,
6474
8547
  example: 'STOCK'
6475
8548
  },
@@ -6516,14 +8589,14 @@ export const $HoldingPnlRowDto = {
6516
8589
  ]
6517
8590
  },
6518
8591
  unrealizedPnlBase: {
6519
- type: 'object',
8592
+ type: 'string',
6520
8593
  description:
6521
8594
  'Unrealized P&L in base currency (Decimal string); null when any FX/price missing',
6522
8595
  nullable: true,
6523
8596
  example: '6000'
6524
8597
  },
6525
8598
  unrealizedPnlPct: {
6526
- type: 'object',
8599
+ type: 'string',
6527
8600
  description: 'Unrealized P&L % (Decimal string)',
6528
8601
  nullable: true,
6529
8602
  example: '25'
@@ -6547,7 +8620,7 @@ export const $HoldingPnlRowDto = {
6547
8620
  ]
6548
8621
  },
6549
8622
  pctOfInvestedAssets: {
6550
- type: 'object',
8623
+ type: 'string',
6551
8624
  description:
6552
8625
  'Share of invested assets % (Decimal string); only for invested chartTokens',
6553
8626
  nullable: true,
@@ -6592,15 +8665,15 @@ export const $HoldingPnlWarningDto = {
6592
8665
  ]
6593
8666
  },
6594
8667
  symbol: {
6595
- type: 'object',
8668
+ type: 'string',
6596
8669
  nullable: true
6597
8670
  },
6598
8671
  accountId: {
6599
- type: 'object',
8672
+ type: 'string',
6600
8673
  nullable: true
6601
8674
  },
6602
8675
  currency: {
6603
- type: 'object',
8676
+ type: 'string',
6604
8677
  nullable: true
6605
8678
  }
6606
8679
  },
@@ -6641,334 +8714,378 @@ export const $HoldingPnlResponseDto = {
6641
8714
  required: ['asOfDate', 'baseCurrency', 'method', 'rows', 'warnings']
6642
8715
  } as const;
6643
8716
 
6644
- export const $CreateBeanPriceDto = {
8717
+ export const $AnonymousLoginDto = {
6645
8718
  type: 'object',
6646
8719
  properties: {
6647
- currency: {
6648
- type: 'string',
6649
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6650
- example: 'USD'
6651
- },
6652
- quoteCurrency: {
8720
+ accessToken: {
6653
8721
  type: 'string',
6654
- description: 'Quote currency (pricing currency, e.g., CNY, EUR)',
6655
- example: 'CNY'
6656
- },
6657
- amount: {
6658
- type: 'number',
6659
- description:
6660
- 'Price amount (MUST be >= 0 per Beancount spec, supports up to 15 decimal places). Zero allowed for conversion entries, negative strictly prohibited.',
6661
- example: 175.5,
6662
- minimum: 0
6663
- },
6664
- date: {
8722
+ description: 'Access token for anonymous login'
8723
+ }
8724
+ },
8725
+ required: ['accessToken']
8726
+ } as const;
8727
+
8728
+ export const $AnonymousLoginResponseDto = {
8729
+ type: 'object',
8730
+ properties: {
8731
+ authToken: {
6665
8732
  type: 'string',
6666
- description: 'Price date (ISO 8601 format)',
6667
- example: '2024-11-05'
6668
- },
6669
- metadata: {
6670
- type: 'object',
6671
- description:
6672
- 'Metadata (validated by Zod schema, max field lengths enforced)',
6673
- example: {
6674
- source: 'MANUAL',
6675
- note: 'Bank valuation report',
6676
- confidence: 0.95
6677
- }
8733
+ description: 'JWT auth token',
8734
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
6678
8735
  }
6679
8736
  },
6680
- required: ['currency', 'quoteCurrency', 'amount', 'date']
8737
+ required: ['authToken']
6681
8738
  } as const;
6682
8739
 
6683
- export const $PriceResponseDto = {
8740
+ export const $ParserContributionMetaDto = {
6684
8741
  type: 'object',
6685
8742
  properties: {
6686
- id: {
8743
+ institution: {
6687
8744
  type: 'string',
6688
- description: 'Unique identifier',
6689
- example: 'uuid-123-456'
8745
+ description: 'Institution slug (lowercase kebab-case)',
8746
+ pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
8747
+ example: 'icbc'
6690
8748
  },
6691
- userId: {
8749
+ region: {
6692
8750
  type: 'string',
6693
- description: 'User ID (owner of the price)',
6694
- example: 'user-123'
8751
+ enum: [
8752
+ 'cn',
8753
+ 'us',
8754
+ 'de',
8755
+ 'fr',
8756
+ 'gb',
8757
+ 'hk',
8758
+ 'jp',
8759
+ 'sg',
8760
+ 'au',
8761
+ 'ca',
8762
+ 'other'
8763
+ ]
6695
8764
  },
6696
- currency: {
8765
+ accountType: {
6697
8766
  type: 'string',
6698
- description: 'Currency being priced (e.g., USD, AAPL, BTC)',
6699
- example: 'BTC'
8767
+ enum: ['checking', 'savings', 'credit', 'debit', 'investment']
6700
8768
  },
6701
- quoteCurrency: {
8769
+ format: {
6702
8770
  type: 'string',
6703
- description: 'Quote currency (pricing currency, e.g., USD, CNY)',
6704
- example: 'USD'
6705
- },
6706
- amount: {
6707
- type: 'number',
6708
- description:
6709
- 'Price amount (corresponds to Beancount Amount.number). Supports up to 15 decimal places.',
6710
- example: 50000
8771
+ enum: ['csv', 'xlsx', 'pdf', 'ofx', 'qif']
6711
8772
  },
6712
- date: {
8773
+ institutionDisplayName: {
6713
8774
  type: 'string',
6714
- description:
6715
- 'Price date (ISO 8601 format). Represents the date this price was valid.',
6716
- example: '2024-01-01',
6717
- format: 'date'
8775
+ example: '中国工商银行'
6718
8776
  },
6719
- meta: {
6720
- type: 'object',
6721
- description:
6722
- 'Metadata (corresponds to Beancount meta field). Contains source, confidence, note, etc.',
6723
- example: {
6724
- source: 'MANUAL',
6725
- note: 'User-defined price',
6726
- confidence: 1
6727
- }
8777
+ encoding: {
8778
+ type: 'string',
8779
+ example: 'utf-8'
6728
8780
  },
6729
- createdAt: {
6730
- format: 'date-time',
8781
+ delimiter: {
6731
8782
  type: 'string',
6732
- description: 'Creation timestamp',
6733
- example: '2024-11-03T10:00:00Z'
8783
+ description: 'CSV delimiter character: ",", ";", "\\t" or "|"'
6734
8784
  },
6735
- updatedAt: {
6736
- format: 'date-time',
8785
+ headerRows: {
8786
+ type: 'number',
8787
+ default: 1,
8788
+ description: 'Header row count; the client omits the field when it is 1'
8789
+ },
8790
+ notes: {
6737
8791
  type: 'string',
6738
- description: 'Last update timestamp',
6739
- example: '2024-11-03T10:00:00Z'
8792
+ maxLength: 2000
6740
8793
  }
6741
8794
  },
6742
- required: [
6743
- 'id',
6744
- 'userId',
6745
- 'currency',
6746
- 'quoteCurrency',
6747
- 'amount',
6748
- 'date',
6749
- 'meta',
6750
- 'createdAt',
6751
- 'updatedAt'
6752
- ]
8795
+ required: ['institution', 'region', 'accountType', 'format']
6753
8796
  } as const;
6754
8797
 
6755
- export const $PriceListResponseDto = {
8798
+ export const $ParserContributionSamplesDto = {
6756
8799
  type: 'object',
6757
8800
  properties: {
6758
- items: {
6759
- description: 'List of prices',
8801
+ rows: {
8802
+ description:
8803
+ 'Client-sanitized sample rows (key = column name, value = cell)',
6760
8804
  type: 'array',
6761
8805
  items: {
6762
- $ref: '#/components/schemas/PriceResponseDto'
8806
+ type: 'object'
6763
8807
  }
6764
8808
  },
6765
- total: {
6766
- type: 'number',
6767
- description: 'Total number of prices',
6768
- example: 42
8809
+ rawHeaders: {
8810
+ type: 'array',
8811
+ items: {
8812
+ type: 'string'
8813
+ }
6769
8814
  }
6770
8815
  },
6771
- required: ['items', 'total']
8816
+ required: ['rows']
6772
8817
  } as const;
6773
8818
 
6774
- export const $UpdateBeanPriceDto = {
8819
+ export const $FieldHintDto = {
6775
8820
  type: 'object',
6776
8821
  properties: {
6777
- currency: {
8822
+ columnName: {
6778
8823
  type: 'string',
6779
- description: 'Currency being priced'
8824
+ example: '交易日期'
6780
8825
  },
6781
- quoteCurrency: {
8826
+ format: {
6782
8827
  type: 'string',
6783
- description: 'Quote currency (pricing currency)'
6784
- },
6785
- amount: {
6786
- type: 'number',
6787
- description: 'Price amount (MUST be >= 0 per Beancount spec)',
6788
- minimum: 0
8828
+ description: 'Date format, e.g. yyyy-MM-dd HH:mm',
8829
+ example: 'yyyy-MM-dd'
6789
8830
  },
6790
- date: {
8831
+ signConvention: {
6791
8832
  type: 'string',
6792
- description: 'Price date (ISO 8601 format)'
8833
+ enum: ['negative-expense', 'positive-expense', 'separate-columns']
6793
8834
  },
6794
- metadata: {
6795
- type: 'object',
6796
- description: 'Metadata'
8835
+ creditColumn: {
8836
+ type: 'string'
8837
+ },
8838
+ debitColumn: {
8839
+ type: 'string'
6797
8840
  }
6798
- }
8841
+ },
8842
+ required: ['columnName']
6799
8843
  } as const;
6800
8844
 
6801
- export const $CurrencyBalanceDto = {
8845
+ export const $ParserContributionFieldHintsDto = {
6802
8846
  type: 'object',
6803
8847
  properties: {
6804
- currency: {
6805
- type: 'string',
6806
- description: 'ISO 4217 currency code',
6807
- example: 'CNY'
8848
+ date: {
8849
+ $ref: '#/components/schemas/FieldHintDto'
8850
+ },
8851
+ amount: {
8852
+ $ref: '#/components/schemas/FieldHintDto'
8853
+ },
8854
+ description: {
8855
+ $ref: '#/components/schemas/FieldHintDto'
6808
8856
  },
6809
8857
  balance: {
6810
- type: 'string',
6811
- description: 'Balance amount',
6812
- example: '500000.00'
8858
+ $ref: '#/components/schemas/FieldHintDto'
8859
+ },
8860
+ payee: {
8861
+ $ref: '#/components/schemas/FieldHintDto'
8862
+ },
8863
+ reference: {
8864
+ $ref: '#/components/schemas/FieldHintDto'
8865
+ },
8866
+ category: {
8867
+ $ref: '#/components/schemas/FieldHintDto'
6813
8868
  }
6814
8869
  },
6815
- required: ['currency', 'balance']
8870
+ required: ['date', 'amount']
6816
8871
  } as const;
6817
8872
 
6818
- export const $TimeSeriesPointDto = {
8873
+ export const $ExpectedTransactionDto = {
6819
8874
  type: 'object',
6820
8875
  properties: {
6821
8876
  date: {
6822
8877
  type: 'string',
6823
- description: 'Date in YYYY-MM-DD format',
6824
- example: '2024-06-15'
8878
+ example: '2026-08-01'
6825
8879
  },
6826
- value: {
8880
+ amount: {
8881
+ type: 'number',
8882
+ example: -45.5
8883
+ },
8884
+ description: {
6827
8885
  type: 'string',
6828
- description: 'Value at this date (in base currency)',
6829
- example: '500000.00'
8886
+ example: '星巴克-***店'
6830
8887
  },
6831
- change: {
6832
- type: 'object',
6833
- description: 'Change from previous point',
6834
- example: '5000.00'
8888
+ payee: {
8889
+ type: 'string'
6835
8890
  },
6836
- byCurrency: {
6837
- description: 'Multi-currency breakdown for this point',
8891
+ category: {
8892
+ type: 'string'
8893
+ }
8894
+ },
8895
+ required: ['date', 'amount', 'description']
8896
+ } as const;
8897
+
8898
+ export const $ParserContributionExamplesDto = {
8899
+ type: 'object',
8900
+ properties: {
8901
+ expectedTransactions: {
6838
8902
  type: 'array',
6839
8903
  items: {
6840
- $ref: '#/components/schemas/CurrencyBalanceDto'
8904
+ $ref: '#/components/schemas/ExpectedTransactionDto'
6841
8905
  }
6842
8906
  }
6843
8907
  },
6844
- required: ['date', 'value']
8908
+ required: ['expectedTransactions']
6845
8909
  } as const;
6846
8910
 
6847
- export const $TrendSummaryDto = {
8911
+ export const $ParserContributionRequestDto = {
6848
8912
  type: 'object',
6849
8913
  properties: {
6850
- startValue: {
6851
- type: 'string',
6852
- description: 'Value at start of period',
6853
- example: '450000.00'
8914
+ meta: {
8915
+ $ref: '#/components/schemas/ParserContributionMetaDto'
6854
8916
  },
6855
- endValue: {
6856
- type: 'string',
6857
- description: 'Value at end of period',
6858
- example: '500000.00'
8917
+ samples: {
8918
+ $ref: '#/components/schemas/ParserContributionSamplesDto'
6859
8919
  },
6860
- totalChange: {
6861
- type: 'string',
6862
- description: 'Total change over period',
6863
- example: '50000.00'
8920
+ fieldHints: {
8921
+ $ref: '#/components/schemas/ParserContributionFieldHintsDto'
6864
8922
  },
6865
- totalChangePercentage: {
6866
- type: 'string',
6867
- description: 'Total change percentage',
6868
- example: '+11.11%'
8923
+ examples: {
8924
+ description: 'Omitted entirely by the client when empty',
8925
+ allOf: [
8926
+ {
8927
+ $ref: '#/components/schemas/ParserContributionExamplesDto'
8928
+ }
8929
+ ]
6869
8930
  }
6870
8931
  },
6871
- required: ['startValue', 'endValue', 'totalChange', 'totalChangePercentage']
8932
+ required: ['meta', 'samples', 'fieldHints']
6872
8933
  } as const;
6873
8934
 
6874
- export const $MultiCurrencyPointDto = {
8935
+ export const $ParserContributionRelayResponseDto = {
6875
8936
  type: 'object',
6876
8937
  properties: {
6877
- date: {
8938
+ issueUrl: {
6878
8939
  type: 'string',
6879
- description: 'Date in YYYY-MM-DD format',
6880
- example: '2024-06-15'
8940
+ example: 'https://github.com/fire-zu/firela-vlt/issues/42'
6881
8941
  },
6882
- byCurrency: {
6883
- description: 'Balances by currency',
6884
- type: 'array',
6885
- items: {
6886
- $ref: '#/components/schemas/CurrencyBalanceDto'
6887
- }
8942
+ issueNumber: {
8943
+ type: 'number',
8944
+ example: 42
6888
8945
  }
6889
8946
  },
6890
- required: ['date', 'byCurrency']
8947
+ required: ['issueUrl', 'issueNumber']
6891
8948
  } as const;
6892
8949
 
6893
- export const $PortfolioTrendsResponseDto = {
8950
+ export const $SymbolSearchResultDto = {
6894
8951
  type: 'object',
6895
8952
  properties: {
6896
- series: {
6897
- description: 'Time series data points',
6898
- type: 'array',
6899
- items: {
6900
- $ref: '#/components/schemas/TimeSeriesPointDto'
6901
- }
8953
+ symbol: {
8954
+ type: 'string',
8955
+ example: 'AAPL'
6902
8956
  },
6903
- summary: {
6904
- description: 'Period summary',
6905
- allOf: [
6906
- {
6907
- $ref: '#/components/schemas/TrendSummaryDto'
6908
- }
6909
- ]
8957
+ name: {
8958
+ type: 'string',
8959
+ example: 'Apple Inc.',
8960
+ nullable: true
6910
8961
  },
6911
- period: {
8962
+ exchange: {
6912
8963
  type: 'string',
6913
- description: 'Period requested',
6914
- example: '6m'
8964
+ example: 'US',
8965
+ nullable: true
6915
8966
  },
6916
- granularity: {
8967
+ assetType: {
6917
8968
  type: 'string',
6918
- description: 'Data granularity',
6919
- example: 'month'
8969
+ description: 'OpenBB asset_type (e.g. stock, etf)',
8970
+ example: 'stock',
8971
+ nullable: true
6920
8972
  },
6921
- currency: {
8973
+ assetClass: {
6922
8974
  type: 'string',
6923
- description: 'Base currency for converted values',
6924
- example: 'CNY'
8975
+ description: 'IGN asset class (region.types.ts ASSET_CLASSES)',
8976
+ example: 'EQUITY',
8977
+ nullable: true
6925
8978
  },
6926
- byCurrency: {
6927
- description:
6928
- 'Multi-currency time series (each point has currency breakdown)',
6929
- type: 'array',
6930
- items: {
6931
- $ref: '#/components/schemas/MultiCurrencyPointDto'
6932
- }
8979
+ assetSubClass: {
8980
+ type: 'string',
8981
+ description: 'IGN asset sub-class (region.types.ts ASSET_SUB_CLASSES)',
8982
+ example: 'STOCK',
8983
+ nullable: true
6933
8984
  },
6934
- warnings: {
6935
- description: 'Exchange rate warnings',
6936
- type: 'array',
6937
- items: {
6938
- $ref: '#/components/schemas/ExchangeRateWarningDto'
6939
- }
8985
+ currency: {
8986
+ type: 'string',
8987
+ description: 'Trading currency (extra_data or inferred from exchange)',
8988
+ example: 'USD',
8989
+ nullable: true
6940
8990
  }
6941
8991
  },
6942
- required: ['series', 'summary', 'period', 'granularity', 'currency']
6943
- } as const;
6944
-
6945
- export const $GenerateSnapshotBody = {
6946
- type: 'object',
6947
- properties: {}
6948
- } as const;
6949
-
6950
- export const $GenerateSnapshotResponse = {
6951
- type: 'object',
6952
- properties: {}
6953
- } as const;
6954
-
6955
- export const $BackfillSnapshotsBody = {
6956
- type: 'object',
6957
- properties: {}
6958
- } as const;
6959
-
6960
- export const $BackfillSnapshotsResponse = {
6961
- type: 'object',
6962
- properties: {}
8992
+ required: ['symbol']
6963
8993
  } as const;
6964
8994
 
6965
- export const $AnonymousLoginDto = {
8995
+ export const $SymbolQuoteDto = {
6966
8996
  type: 'object',
6967
8997
  properties: {
6968
- accessToken: {
8998
+ symbol: {
6969
8999
  type: 'string',
6970
- description: 'Access token for anonymous login'
9000
+ example: 'AAPL'
9001
+ },
9002
+ name: {
9003
+ type: 'string',
9004
+ example: 'Apple Inc.',
9005
+ nullable: true
9006
+ },
9007
+ exchange: {
9008
+ type: 'string',
9009
+ example: 'US',
9010
+ nullable: true
9011
+ },
9012
+ assetType: {
9013
+ type: 'string',
9014
+ description: 'OpenBB asset_type',
9015
+ example: 'stock',
9016
+ nullable: true
9017
+ },
9018
+ assetClass: {
9019
+ type: 'string',
9020
+ description: 'IGN asset class',
9021
+ example: 'EQUITY',
9022
+ nullable: true
9023
+ },
9024
+ assetSubClass: {
9025
+ type: 'string',
9026
+ description: 'IGN asset sub-class',
9027
+ example: 'STOCK',
9028
+ nullable: true
9029
+ },
9030
+ currency: {
9031
+ type: 'string',
9032
+ description: 'Trading currency (extra_data or inferred from exchange)',
9033
+ example: 'USD',
9034
+ nullable: true
9035
+ },
9036
+ price: {
9037
+ type: 'string',
9038
+ description: 'Latest price (Decimal string)',
9039
+ example: '189.84',
9040
+ nullable: true
9041
+ },
9042
+ priceDate: {
9043
+ type: 'string',
9044
+ description: 'Date the price was observed (ISO yyyy-MM-dd)',
9045
+ example: '2026-08-05',
9046
+ nullable: true
9047
+ },
9048
+ changePercent: {
9049
+ type: 'number',
9050
+ description:
9051
+ '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.',
9052
+ example: 1.7,
9053
+ nullable: true
9054
+ },
9055
+ prevClose: {
9056
+ type: 'string',
9057
+ description: 'Previous close (Decimal string)',
9058
+ nullable: true
9059
+ },
9060
+ open: {
9061
+ type: 'string',
9062
+ description: 'Day open (Decimal string)',
9063
+ nullable: true
9064
+ },
9065
+ high: {
9066
+ type: 'string',
9067
+ description: 'Day high (Decimal string)',
9068
+ nullable: true
9069
+ },
9070
+ low: {
9071
+ type: 'string',
9072
+ description: 'Day low (Decimal string)',
9073
+ nullable: true
9074
+ },
9075
+ volume: {
9076
+ type: 'string',
9077
+ description: 'Day volume (Decimal string)',
9078
+ nullable: true
9079
+ },
9080
+ yearHigh: {
9081
+ type: 'string',
9082
+ description: '52-week high (Decimal string)',
9083
+ nullable: true
9084
+ },
9085
+ yearLow: {
9086
+ type: 'string',
9087
+ description: '52-week low (Decimal string)',
9088
+ nullable: true
6971
9089
  }
6972
- },
6973
- required: ['accessToken']
9090
+ }
6974
9091
  } as const;