@model-match/api 3.16.0 → 3.17.0

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.
package/openapi.json CHANGED
@@ -3189,6 +3189,16 @@
3189
3189
  "period": {
3190
3190
  "$ref": "#/components/schemas/Period"
3191
3191
  },
3192
+ "segment": {
3193
+ "allOf": [
3194
+ {
3195
+ "$ref": "#/components/schemas/BranchSlice"
3196
+ },
3197
+ {
3198
+ "description": "Optional second dimension. Each bucket of `slice` then carries `segments`: the same `measure` broken down by this dimension (e.g. `slice: \"loanType\", segment: \"lender\"` is loan type × lender in one call), top 25 per bucket ranked by `measure`, with the remainder counted in `segmentsOmittedCount`. Omit it and the response is the one-dimensional chart, unchanged. Must differ from `slice`, and cannot be combined with `size` / `after` paging (400)."
3199
+ }
3200
+ ]
3201
+ },
3192
3202
  "slice": {
3193
3203
  "$ref": "#/components/schemas/BranchSlice"
3194
3204
  }
@@ -4144,7 +4154,24 @@
4144
4154
  ],
4145
4155
  "type": "string"
4146
4156
  },
4157
+ "BranchSegment": {
4158
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`. Time-series only: `titleCompany` groups on the title company named on the deed, with spellings of one company merged per bucket (`label` = most-used spelling across the response, `id` = normalized name — match on `id`) and \"none available\"-style placeholders excluded.",
4159
+ "enum": [
4160
+ "transactionType",
4161
+ "loanType",
4162
+ "city",
4163
+ "county",
4164
+ "state",
4165
+ "zip",
4166
+ "lender",
4167
+ "propertyType",
4168
+ "conforming",
4169
+ "titleCompany"
4170
+ ],
4171
+ "type": "string"
4172
+ },
4147
4173
  "BranchSlice": {
4174
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`.",
4148
4175
  "enum": [
4149
4176
  "transactionType",
4150
4177
  "loanType",
@@ -4152,7 +4179,9 @@
4152
4179
  "county",
4153
4180
  "state",
4154
4181
  "zip",
4155
- "lender"
4182
+ "lender",
4183
+ "propertyType",
4184
+ "conforming"
4156
4185
  ],
4157
4186
  "type": "string"
4158
4187
  },
@@ -4918,7 +4947,7 @@
4918
4947
  "$ref": "#/components/schemas/Period"
4919
4948
  },
4920
4949
  "segment": {
4921
- "$ref": "#/components/schemas/BranchSlice"
4950
+ "$ref": "#/components/schemas/BranchSegment"
4922
4951
  }
4923
4952
  },
4924
4953
  "required": [
@@ -5589,18 +5618,23 @@
5589
5618
  "type": "number"
5590
5619
  },
5591
5620
  "id": {
5592
- "description": "Machine value behind `label` when the bucket key is an id rather than a name (today: the `lender` slice/segment, where `label` is the dictionary display name). Pass `id` — not `label` — back as a `lender` filter value. Absent on slices whose `label` is already the machine value.",
5621
+ "description": "Machine value behind `label` when the bucket key is not itself the label. `lender`: `label` is the dictionary display name and `id` the normalized lender key — pass `id`, not `label`, back as a `lender` filter value. `conforming`: `label` is `conforming` / `nonConforming` and `id` the raw `true` / `false` flag. `titleCompany` (time-series segment): `label` is the most-used spelling and `id` the normalized name every spelling was merged under — match segments across buckets on `id`. Absent on slices whose `label` is already the machine value.",
5593
5622
  "type": "string"
5594
5623
  },
5595
5624
  "label": {
5596
5625
  "type": "string"
5597
5626
  },
5598
5627
  "segments": {
5628
+ "description": "Present only when the request named a `segment`: this bucket broken down by that second dimension, ranked by the same `measure` and capped at 25 per bucket (the rest is counted in `segmentsOmittedCount`). Documents with no value for the segment are not grouped and appear nowhere here, so segment `count`s sum to at most the bucket's `count`.",
5599
5629
  "items": {
5600
5630
  "$ref": "#/components/schemas/ChartSegment"
5601
5631
  },
5602
5632
  "type": "array"
5603
5633
  },
5634
+ "segmentsOmittedCount": {
5635
+ "description": "Present only with `segments`: documents in this bucket whose segment value fell past the 25-per-bucket cap. A document COUNT, never a dollar figure. `0` means `segments` is the complete breakdown of every document that carries a segment value.",
5636
+ "type": "number"
5637
+ },
5604
5638
  "unattributed": {
5605
5639
  "description": "Present and `true` on at most one bucket, which is NOT a real category: it collects the documents this breakdown could not attribute to any value of the slice, and its `label` (`\"(unknown)\"`) is prose, not an identifier. Filter on THIS field, never on the label — a consumer plotting a map or joining the label to a real code must drop this row. It is always the LAST element of `data`, appended after ranking rather than placed by value, so it is the one bucket that does not obey the ordering `measure` describes. It exists so the bars account for the same population the summary reports: a document whose slice field holds no indexed term is absent from a terms breakdown entirely rather than forming a small bucket, so without it a chart silently omits part of its own corpus. That reconciliation is exact for `units`; on an average it is not, because buckets under the five-document floor are dropped and a slice with more than 200 distinct values still truncates — neither loss is collected in this row (the truncated tail is reported response-wide as `omittedCount`; the floor is reported nowhere). Absent when there is nothing unattributed, never `false` and never a zero-valued row.",
5606
5640
  "enum": [
@@ -5653,6 +5687,7 @@
5653
5687
  "type": "number"
5654
5688
  },
5655
5689
  "segment": {
5690
+ "description": "The second dimension echoed from the request, or `null` for a one-dimensional chart (then no bucket carries `segments`).",
5656
5691
  "nullable": true,
5657
5692
  "type": "string"
5658
5693
  },
@@ -5678,16 +5713,26 @@
5678
5713
  },
5679
5714
  "ChartSegment": {
5680
5715
  "properties": {
5716
+ "count": {
5717
+ "description": "Documents in this segment of the parent bucket — the denominator behind `value`, whatever the measure.",
5718
+ "type": "number"
5719
+ },
5720
+ "id": {
5721
+ "description": "Machine value behind `label` when the bucket key is not itself the label. `lender`: `label` is the dictionary display name and `id` the normalized lender key — pass `id`, not `label`, back as a `lender` filter value. `conforming`: `label` is `conforming` / `nonConforming` and `id` the raw `true` / `false` flag. `titleCompany` (time-series segment): `label` is the most-used spelling and `id` the normalized name every spelling was merged under — match segments across buckets on `id`. Absent on slices whose `label` is already the machine value.",
5722
+ "type": "string"
5723
+ },
5681
5724
  "label": {
5682
5725
  "type": "string"
5683
5726
  },
5684
5727
  "value": {
5728
+ "description": "The requested `measure` over the documents in BOTH this segment and its parent bucket.",
5685
5729
  "type": "number"
5686
5730
  }
5687
5731
  },
5688
5732
  "required": [
5689
5733
  "label",
5690
- "value"
5734
+ "value",
5735
+ "count"
5691
5736
  ],
5692
5737
  "type": "object"
5693
5738
  },
@@ -6514,6 +6559,16 @@
6514
6559
  "period": {
6515
6560
  "$ref": "#/components/schemas/Period"
6516
6561
  },
6562
+ "segment": {
6563
+ "allOf": [
6564
+ {
6565
+ "$ref": "#/components/schemas/CompanySlice"
6566
+ },
6567
+ {
6568
+ "description": "Optional second dimension. Each bucket of `slice` then carries `segments`: the same `measure` broken down by this dimension (e.g. `slice: \"loanType\", segment: \"lender\"` is loan type × lender in one call), top 25 per bucket ranked by `measure`, with the remainder counted in `segmentsOmittedCount`. Omit it and the response is the one-dimensional chart, unchanged. Must differ from `slice`, and cannot be combined with `size` / `after` paging (400)."
6569
+ }
6570
+ ]
6571
+ },
6517
6572
  "slice": {
6518
6573
  "$ref": "#/components/schemas/CompanySlice"
6519
6574
  }
@@ -8828,7 +8883,24 @@
8828
8883
  },
8829
8884
  "type": "object"
8830
8885
  },
8886
+ "CompanySegment": {
8887
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`. Time-series only: `titleCompany` groups on the title company named on the deed, with spellings of one company merged per bucket (`label` = most-used spelling across the response, `id` = normalized name — match on `id`) and \"none available\"-style placeholders excluded.",
8888
+ "enum": [
8889
+ "transactionType",
8890
+ "loanType",
8891
+ "city",
8892
+ "county",
8893
+ "state",
8894
+ "zip",
8895
+ "lender",
8896
+ "propertyType",
8897
+ "conforming",
8898
+ "titleCompany"
8899
+ ],
8900
+ "type": "string"
8901
+ },
8831
8902
  "CompanySlice": {
8903
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`.",
8832
8904
  "enum": [
8833
8905
  "transactionType",
8834
8906
  "loanType",
@@ -8836,7 +8908,9 @@
8836
8908
  "county",
8837
8909
  "state",
8838
8910
  "zip",
8839
- "lender"
8911
+ "lender",
8912
+ "propertyType",
8913
+ "conforming"
8840
8914
  ],
8841
8915
  "type": "string"
8842
8916
  },
@@ -10308,7 +10382,7 @@
10308
10382
  "$ref": "#/components/schemas/Period"
10309
10383
  },
10310
10384
  "segment": {
10311
- "$ref": "#/components/schemas/CompanySlice"
10385
+ "$ref": "#/components/schemas/CompanySegment"
10312
10386
  }
10313
10387
  },
10314
10388
  "type": "object"
@@ -10572,6 +10646,10 @@
10572
10646
  },
10573
10647
  "type": "array"
10574
10648
  },
10649
+ "occurredAt": {
10650
+ "nullable": true,
10651
+ "type": "number"
10652
+ },
10575
10653
  "parentId": {
10576
10654
  "nullable": true,
10577
10655
  "type": "string"
@@ -10601,7 +10679,8 @@
10601
10679
  "actorEmail",
10602
10680
  "actorImage",
10603
10681
  "createdAt",
10604
- "editedAt"
10682
+ "editedAt",
10683
+ "occurredAt"
10605
10684
  ],
10606
10685
  "type": "object"
10607
10686
  },
@@ -11738,6 +11817,25 @@
11738
11817
  ],
11739
11818
  "type": "object"
11740
11819
  },
11820
+ "CrmEditLoggedActivityResult": {
11821
+ "properties": {
11822
+ "data": {
11823
+ "properties": {
11824
+ "updated": {
11825
+ "type": "boolean"
11826
+ }
11827
+ },
11828
+ "required": [
11829
+ "updated"
11830
+ ],
11831
+ "type": "object"
11832
+ }
11833
+ },
11834
+ "required": [
11835
+ "data"
11836
+ ],
11837
+ "type": "object"
11838
+ },
11741
11839
  "CrmEditNoteResult": {
11742
11840
  "properties": {
11743
11841
  "data": {
@@ -13032,6 +13130,34 @@
13032
13130
  ],
13033
13131
  "type": "object"
13034
13132
  },
13133
+ "CrmLogActivityResult": {
13134
+ "properties": {
13135
+ "data": {
13136
+ "properties": {
13137
+ "id": {
13138
+ "type": "string"
13139
+ }
13140
+ },
13141
+ "required": [
13142
+ "id"
13143
+ ],
13144
+ "type": "object"
13145
+ }
13146
+ },
13147
+ "required": [
13148
+ "data"
13149
+ ],
13150
+ "type": "object"
13151
+ },
13152
+ "CrmLoggedActivityType": {
13153
+ "enum": [
13154
+ "call",
13155
+ "meeting",
13156
+ "email",
13157
+ "task"
13158
+ ],
13159
+ "type": "string"
13160
+ },
13035
13161
  "CrmMailProvider": {
13036
13162
  "enum": [
13037
13163
  "google",
@@ -18965,6 +19091,16 @@
18965
19091
  "period": {
18966
19092
  "$ref": "#/components/schemas/DatedPeriod"
18967
19093
  },
19094
+ "segment": {
19095
+ "allOf": [
19096
+ {
19097
+ "$ref": "#/components/schemas/LoanSlice"
19098
+ },
19099
+ {
19100
+ "description": "Optional second dimension. Each bucket of `slice` then carries `segments`: the same `measure` broken down by this dimension (e.g. `slice: \"loanType\", segment: \"lender\"` is loan type × lender in one call), top 25 per bucket ranked by `measure`, with the remainder counted in `segmentsOmittedCount`. Omit it and the response is the one-dimensional chart, unchanged. Must differ from `slice`, and cannot be combined with `size` / `after` paging (400)."
19101
+ }
19102
+ ]
19103
+ },
18968
19104
  "size": {
18969
19105
  "description": "PARTY slices only (`originator`, `broker`, `loanCompany`; 400 on any other slice). Page size for the ranking, 1–1000. Passing `size` (or `after`) turns on paging: the response gains `total` / `totalApproximate` and, when more buckets remain, `nextCursor`, and drops `omittedCount`. Buckets stay ranked by `measure` descending (ties broken by key), and bucket labels stay NMLS ids. Default when only `after` is sent: 200, the fixed chart's bucket count. Paging reaches the top 10,000 ranked buckets.",
18970
19106
  "maximum": 1000,
@@ -19448,7 +19584,27 @@
19448
19584
  ],
19449
19585
  "type": "object"
19450
19586
  },
19587
+ "LoanSegment": {
19588
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`. Time-series only: `titleCompany` groups on the title company named on the deed, with spellings of one company merged per bucket (`label` = most-used spelling across the response, `id` = normalized name — match on `id`) and \"none available\"-style placeholders excluded.",
19589
+ "enum": [
19590
+ "transactionType",
19591
+ "loanType",
19592
+ "city",
19593
+ "county",
19594
+ "state",
19595
+ "zip",
19596
+ "lender",
19597
+ "propertyType",
19598
+ "conforming",
19599
+ "broker",
19600
+ "loanCompany",
19601
+ "originator",
19602
+ "titleCompany"
19603
+ ],
19604
+ "type": "string"
19605
+ },
19451
19606
  "LoanSlice": {
19607
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`.",
19452
19608
  "enum": [
19453
19609
  "transactionType",
19454
19610
  "loanType",
@@ -19457,6 +19613,8 @@
19457
19613
  "state",
19458
19614
  "zip",
19459
19615
  "lender",
19616
+ "propertyType",
19617
+ "conforming",
19460
19618
  "broker",
19461
19619
  "loanCompany",
19462
19620
  "originator"
@@ -20220,7 +20378,7 @@
20220
20378
  "$ref": "#/components/schemas/DatedPeriod"
20221
20379
  },
20222
20380
  "segment": {
20223
- "$ref": "#/components/schemas/LoanSlice"
20381
+ "$ref": "#/components/schemas/LoanSegment"
20224
20382
  }
20225
20383
  },
20226
20384
  "type": "object"
@@ -23816,6 +23974,16 @@
23816
23974
  "period": {
23817
23975
  "$ref": "#/components/schemas/Period"
23818
23976
  },
23977
+ "segment": {
23978
+ "allOf": [
23979
+ {
23980
+ "$ref": "#/components/schemas/OriginatorSlice"
23981
+ },
23982
+ {
23983
+ "description": "Optional second dimension. Each bucket of `slice` then carries `segments`: the same `measure` broken down by this dimension (e.g. `slice: \"loanType\", segment: \"lender\"` is loan type × lender in one call), top 25 per bucket ranked by `measure`, with the remainder counted in `segmentsOmittedCount`. Omit it and the response is the one-dimensional chart, unchanged. Must differ from `slice`, and cannot be combined with `size` / `after` paging (400)."
23984
+ }
23985
+ ]
23986
+ },
23819
23987
  "slice": {
23820
23988
  "$ref": "#/components/schemas/OriginatorSlice"
23821
23989
  }
@@ -24232,6 +24400,16 @@
24232
24400
  "nullable": true,
24233
24401
  "type": "number"
24234
24402
  },
24403
+ "careerYears": {
24404
+ "description": "Years since the earliest employer start on `records`, to 0.1 — the career length the recruiting signal is judged on. Null when no record carries a start date.",
24405
+ "nullable": true,
24406
+ "type": "number"
24407
+ },
24408
+ "currentTenureYears": {
24409
+ "description": "Years at the current employer (the first employer with an active record), from its earliest record start to today, to 0.1. Null when no employer is active or its start date is unknown. A 365-day year, matching the profile's recruiting signal.",
24410
+ "nullable": true,
24411
+ "type": "number"
24412
+ },
24235
24413
  "employerChanges": {
24236
24414
  "nullable": true,
24237
24415
  "type": "number"
@@ -24242,6 +24420,37 @@
24242
24420
  },
24243
24421
  "type": "array"
24244
24422
  },
24423
+ "recruitingSignal": {
24424
+ "description": "One-line read of this career for recruiting, rule-based over `records` (employers grouped by company NMLS id): no records → `none`; over half the employers missing a start date → `incompleteData`; under 2 career years with one employer → `limitedHistory`; one employer for 2+ years → `singleEmployerCareer`; 5+ years at the current employer with ≤3 employers → `longTermStability`; 3+ employers at ≥0.6 per career year → `highMobility`; under 1 year at the current employer with >1 employer → `recentMove`; 3-4 employers over 5+ years → `multiSponsorships`; otherwise `establishedAtCurrent`. First matching rule wins. `label`/`subtext` are display copy.",
24425
+ "properties": {
24426
+ "code": {
24427
+ "enum": [
24428
+ "singleEmployerCareer",
24429
+ "longTermStability",
24430
+ "establishedAtCurrent",
24431
+ "recentMove",
24432
+ "highMobility",
24433
+ "multiSponsorships",
24434
+ "limitedHistory",
24435
+ "incompleteData",
24436
+ "none"
24437
+ ],
24438
+ "type": "string"
24439
+ },
24440
+ "label": {
24441
+ "type": "string"
24442
+ },
24443
+ "subtext": {
24444
+ "type": "string"
24445
+ }
24446
+ },
24447
+ "required": [
24448
+ "code",
24449
+ "label",
24450
+ "subtext"
24451
+ ],
24452
+ "type": "object"
24453
+ },
24245
24454
  "totalEmployers": {
24246
24455
  "type": "number"
24247
24456
  },
@@ -24284,6 +24493,9 @@
24284
24493
  "nullable": true,
24285
24494
  "type": "array"
24286
24495
  },
24496
+ "profileInsights": {
24497
+ "$ref": "#/components/schemas/OriginatorProfileInsights"
24498
+ },
24287
24499
  "registration": {
24288
24500
  "$ref": "#/components/schemas/OriginatorRegistration"
24289
24501
  },
@@ -26077,6 +26289,41 @@
26077
26289
  ],
26078
26290
  "type": "object"
26079
26291
  },
26292
+ "OriginatorProfileInsights": {
26293
+ "description": "Profile read-outs computed server-side from the LO's production in the requested `period` (default `last12Months`) — the same `volume` / `units` this response reports. Labels and descriptions are display copy; branch on the enum fields.",
26294
+ "properties": {
26295
+ "producerTier": {
26296
+ "description": "From the period's units and volume, first match wins: `topProducer` ≥ 60 units OR ≥ $25M; `strongProducer` ≥ 36 units OR ≥ $12M; `midTier` ≥ 18 units OR ≥ $5M; else `lowVolume`.",
26297
+ "properties": {
26298
+ "description": {
26299
+ "type": "string"
26300
+ },
26301
+ "label": {
26302
+ "type": "string"
26303
+ },
26304
+ "tier": {
26305
+ "enum": [
26306
+ "topProducer",
26307
+ "strongProducer",
26308
+ "midTier",
26309
+ "lowVolume"
26310
+ ],
26311
+ "type": "string"
26312
+ }
26313
+ },
26314
+ "required": [
26315
+ "tier",
26316
+ "label",
26317
+ "description"
26318
+ ],
26319
+ "type": "object"
26320
+ }
26321
+ },
26322
+ "required": [
26323
+ "producerTier"
26324
+ ],
26325
+ "type": "object"
26326
+ },
26080
26327
  "OriginatorRegistration": {
26081
26328
  "nullable": true,
26082
26329
  "properties": {
@@ -26149,7 +26396,24 @@
26149
26396
  },
26150
26397
  "type": "object"
26151
26398
  },
26399
+ "OriginatorSegment": {
26400
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`. Time-series only: `titleCompany` groups on the title company named on the deed, with spellings of one company merged per bucket (`label` = most-used spelling across the response, `id` = normalized name — match on `id`) and \"none available\"-style placeholders excluded.",
26401
+ "enum": [
26402
+ "transactionType",
26403
+ "loanType",
26404
+ "city",
26405
+ "county",
26406
+ "state",
26407
+ "zip",
26408
+ "lender",
26409
+ "propertyType",
26410
+ "conforming",
26411
+ "titleCompany"
26412
+ ],
26413
+ "type": "string"
26414
+ },
26152
26415
  "OriginatorSlice": {
26416
+ "description": "Dimension to group by. Most are the loan's own field (`loanType`, `transactionType`, `state`, …). `lender` groups on the normalized lender (label = display name, `id` = filter key). `propertyType` is the recorder's land-use code of the collateral (`SFR` single-family, `PUD` planned unit development, `CND` condo, `RES` residential-other, `MFD` multi-family dwelling, `2ND` second home, `MFG` manufactured, `TWN` townhouse, `LAN` land, `COM` commercial, …); loans with no code are not grouped (≈20% of recent loans). `conforming` splits on the conforming-loan-limit flag: `conforming` vs `nonConforming` (above the county conforming limit — i.e. jumbo, not Non-QM), raw `true`/`false` on `id`.",
26153
26417
  "enum": [
26154
26418
  "transactionType",
26155
26419
  "loanType",
@@ -26157,7 +26421,9 @@
26157
26421
  "county",
26158
26422
  "state",
26159
26423
  "zip",
26160
- "lender"
26424
+ "lender",
26425
+ "propertyType",
26426
+ "conforming"
26161
26427
  ],
26162
26428
  "type": "string"
26163
26429
  },
@@ -27080,7 +27346,7 @@
27080
27346
  "$ref": "#/components/schemas/Period"
27081
27347
  },
27082
27348
  "segment": {
27083
- "$ref": "#/components/schemas/OriginatorSlice"
27349
+ "$ref": "#/components/schemas/OriginatorSegment"
27084
27350
  }
27085
27351
  },
27086
27352
  "type": "object"
@@ -31445,7 +31711,7 @@
31445
31711
  "SimpleTimeSeriesSegment": {
31446
31712
  "properties": {
31447
31713
  "id": {
31448
- "description": "Machine value behind `label` when the bucket key is an id rather than a name (today: the `lender` slice/segment, where `label` is the dictionary display name). Pass `id` — not `label` — back as a `lender` filter value. Absent on slices whose `label` is already the machine value.",
31714
+ "description": "Machine value behind `label` when the bucket key is not itself the label. `lender`: `label` is the dictionary display name and `id` the normalized lender key — pass `id`, not `label`, back as a `lender` filter value. `conforming`: `label` is `conforming` / `nonConforming` and `id` the raw `true` / `false` flag. `titleCompany` (time-series segment): `label` is the most-used spelling and `id` the normalized name every spelling was merged under — match segments across buckets on `id`. Absent on slices whose `label` is already the machine value.",
31449
31715
  "type": "string"
31450
31716
  },
31451
31717
  "label": {
@@ -32310,7 +32576,7 @@
32310
32576
  "info": {
32311
32577
  "description": "ModelMatch API — mortgage and real-estate market data.\n\n## Bulk delivery limits\n\nThe `POST /{entity}/bulk-delivery` endpoints export a full result set to\nS3 as NDJSON, JSON, CSV, or Parquet.\n\n- **Up to 1,000 rows per delivery: no entitlement required.** Available to\n every authenticated caller.\n- **Above 1,000 rows: requires the entity's `bulk-delivery.{entity}`\n entitlement** (e.g. `bulk-delivery.loans`). The grant's scope sets your\n row ceiling and may also restrict which rows and fields you receive.\n\nA delivery that would exceed the limit available to you is rejected with\n`403 entitlement_missing`, reporting the matched row count and the ceiling\nthat applied. To stay under the limit, pass `limit` — the job then delivers\n(and bills for) only the first `limit` rows of a broader query.\n\n**Need a higher limit?** Email support@modelmatch.com to request the\nentitlement. Limits are raised per organization and per entity.\n\nBulk delivery is billed at 1 credit per delivered row whether or not\nan entitlement is held.",
32312
32578
  "title": "ModelMatch API",
32313
- "version": "3.62.0"
32579
+ "version": "3.63.0"
32314
32580
  },
32315
32581
  "openapi": "3.1.0",
32316
32582
  "paths": {
@@ -35628,6 +35894,99 @@
35628
35894
  ]
35629
35895
  }
35630
35896
  },
35897
+ "/originators/{nmlsId}/breakdowns/title-companies": {
35898
+ "post": {
35899
+ "description": "Ranks the title companies on this entity's loans (recorded deeds, `recording_date` window — the same population and period axis as every other loans breakdown). Spellings of one company are MERGED: `label` is its most-used spelling, `id` the normalized name every spelling was grouped under (legal-form and filler words such as `inc`, `llc`, `co`, `company`, `title`, `escrow` removed; `natl` → `national`). Placeholder entries meaning \"no title company recorded\" (`none available` and its misspellings, `unknown`, `n/a`, …) and names shorter than 3 characters are excluded rather than ranked. `attorney` and `accommodation` are kept: they say who closed. `pctUnits` / `pctVolume` are shares of the entity's WHOLE loan population in the window, including loans with no title company, so they do not sum to 100 across the page. Pages of up to 100 via `pagination`.",
35900
+ "operationId": "originatorTitleCompanies",
35901
+ "parameters": [
35902
+ {
35903
+ "description": "Originator NMLS ID",
35904
+ "in": "path",
35905
+ "name": "nmlsId",
35906
+ "required": true,
35907
+ "schema": {
35908
+ "description": "Originator NMLS ID",
35909
+ "type": "string"
35910
+ }
35911
+ }
35912
+ ],
35913
+ "requestBody": {
35914
+ "content": {
35915
+ "application/json": {
35916
+ "schema": {
35917
+ "$ref": "#/components/schemas/BreakdownRequest"
35918
+ }
35919
+ }
35920
+ }
35921
+ },
35922
+ "responses": {
35923
+ "200": {
35924
+ "content": {
35925
+ "application/json": {
35926
+ "schema": {
35927
+ "$ref": "#/components/schemas/BreakdownResponse"
35928
+ }
35929
+ }
35930
+ },
35931
+ "description": "Breakdown of the entity's loans by title company"
35932
+ },
35933
+ "400": {
35934
+ "content": {
35935
+ "application/json": {
35936
+ "schema": {
35937
+ "$ref": "#/components/schemas/ErrorResponse"
35938
+ }
35939
+ }
35940
+ },
35941
+ "description": "Validation error"
35942
+ },
35943
+ "401": {
35944
+ "content": {
35945
+ "application/json": {
35946
+ "schema": {
35947
+ "$ref": "#/components/schemas/ErrorResponse"
35948
+ }
35949
+ }
35950
+ },
35951
+ "description": "Unauthorized"
35952
+ },
35953
+ "403": {
35954
+ "content": {
35955
+ "application/json": {
35956
+ "schema": {
35957
+ "$ref": "#/components/schemas/ErrorResponse"
35958
+ }
35959
+ }
35960
+ },
35961
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
35962
+ },
35963
+ "404": {
35964
+ "content": {
35965
+ "application/json": {
35966
+ "schema": {
35967
+ "$ref": "#/components/schemas/ErrorResponse"
35968
+ }
35969
+ }
35970
+ },
35971
+ "description": "Not found"
35972
+ },
35973
+ "413": {
35974
+ "content": {
35975
+ "application/json": {
35976
+ "schema": {
35977
+ "$ref": "#/components/schemas/PayloadTooLarge"
35978
+ }
35979
+ }
35980
+ },
35981
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
35982
+ }
35983
+ },
35984
+ "summary": "Title companies on this LO's loans (spellings merged)",
35985
+ "tags": [
35986
+ "Originators"
35987
+ ]
35988
+ }
35989
+ },
35631
35990
  "/originators/{nmlsId}/breakdowns/zip-codes": {
35632
35991
  "post": {
35633
35992
  "operationId": "originatorZipCodes",
@@ -40761,6 +41120,99 @@
40761
41120
  ]
40762
41121
  }
40763
41122
  },
41123
+ "/v1/branches/{nmlsId}/breakdowns/title-companies": {
41124
+ "post": {
41125
+ "description": "Ranks the title companies on this entity's loans (recorded deeds, `recording_date` window — the same population and period axis as every other loans breakdown). Spellings of one company are MERGED: `label` is its most-used spelling, `id` the normalized name every spelling was grouped under (legal-form and filler words such as `inc`, `llc`, `co`, `company`, `title`, `escrow` removed; `natl` → `national`). Placeholder entries meaning \"no title company recorded\" (`none available` and its misspellings, `unknown`, `n/a`, …) and names shorter than 3 characters are excluded rather than ranked. `attorney` and `accommodation` are kept: they say who closed. `pctUnits` / `pctVolume` are shares of the entity's WHOLE loan population in the window, including loans with no title company, so they do not sum to 100 across the page. Pages of up to 100 via `pagination`.",
41126
+ "operationId": "branchTitleCompanies",
41127
+ "parameters": [
41128
+ {
41129
+ "description": "Branch NMLS ID",
41130
+ "in": "path",
41131
+ "name": "nmlsId",
41132
+ "required": true,
41133
+ "schema": {
41134
+ "description": "Branch NMLS ID",
41135
+ "type": "string"
41136
+ }
41137
+ }
41138
+ ],
41139
+ "requestBody": {
41140
+ "content": {
41141
+ "application/json": {
41142
+ "schema": {
41143
+ "$ref": "#/components/schemas/BreakdownRequest"
41144
+ }
41145
+ }
41146
+ }
41147
+ },
41148
+ "responses": {
41149
+ "200": {
41150
+ "content": {
41151
+ "application/json": {
41152
+ "schema": {
41153
+ "$ref": "#/components/schemas/BreakdownResponse"
41154
+ }
41155
+ }
41156
+ },
41157
+ "description": "Breakdown of the entity's loans by title company"
41158
+ },
41159
+ "400": {
41160
+ "content": {
41161
+ "application/json": {
41162
+ "schema": {
41163
+ "$ref": "#/components/schemas/ErrorResponse"
41164
+ }
41165
+ }
41166
+ },
41167
+ "description": "Validation error"
41168
+ },
41169
+ "401": {
41170
+ "content": {
41171
+ "application/json": {
41172
+ "schema": {
41173
+ "$ref": "#/components/schemas/ErrorResponse"
41174
+ }
41175
+ }
41176
+ },
41177
+ "description": "Unauthorized"
41178
+ },
41179
+ "403": {
41180
+ "content": {
41181
+ "application/json": {
41182
+ "schema": {
41183
+ "$ref": "#/components/schemas/ErrorResponse"
41184
+ }
41185
+ }
41186
+ },
41187
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
41188
+ },
41189
+ "404": {
41190
+ "content": {
41191
+ "application/json": {
41192
+ "schema": {
41193
+ "$ref": "#/components/schemas/ErrorResponse"
41194
+ }
41195
+ }
41196
+ },
41197
+ "description": "Not found"
41198
+ },
41199
+ "413": {
41200
+ "content": {
41201
+ "application/json": {
41202
+ "schema": {
41203
+ "$ref": "#/components/schemas/PayloadTooLarge"
41204
+ }
41205
+ }
41206
+ },
41207
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
41208
+ }
41209
+ },
41210
+ "summary": "Title companies on this branch's loans (spellings merged)",
41211
+ "tags": [
41212
+ "Branches"
41213
+ ]
41214
+ }
41215
+ },
40764
41216
  "/v1/branches/{nmlsId}/loans": {
40765
41217
  "post": {
40766
41218
  "description": "Returns the individual loans attributed to this branch office — the branch's own book, not its parent company's. Accepts the same filters, sorts and pagination as the main loan search. Attribution comes from public record, so this is the branch's recorded footprint and can be smaller than the production totals on `GET /v1/branches/{nmlsId}`.",
@@ -42694,6 +43146,99 @@
42694
43146
  ]
42695
43147
  }
42696
43148
  },
43149
+ "/v1/companies/{nmlsId}/breakdowns/title-companies": {
43150
+ "post": {
43151
+ "description": "Ranks the title companies on this entity's loans (recorded deeds, `recording_date` window — the same population and period axis as every other loans breakdown). Spellings of one company are MERGED: `label` is its most-used spelling, `id` the normalized name every spelling was grouped under (legal-form and filler words such as `inc`, `llc`, `co`, `company`, `title`, `escrow` removed; `natl` → `national`). Placeholder entries meaning \"no title company recorded\" (`none available` and its misspellings, `unknown`, `n/a`, …) and names shorter than 3 characters are excluded rather than ranked. `attorney` and `accommodation` are kept: they say who closed. `pctUnits` / `pctVolume` are shares of the entity's WHOLE loan population in the window, including loans with no title company, so they do not sum to 100 across the page. Pages of up to 100 via `pagination`.",
43152
+ "operationId": "companyTitleCompanies",
43153
+ "parameters": [
43154
+ {
43155
+ "description": "Company NMLS ID",
43156
+ "in": "path",
43157
+ "name": "nmlsId",
43158
+ "required": true,
43159
+ "schema": {
43160
+ "description": "Company NMLS ID",
43161
+ "type": "string"
43162
+ }
43163
+ }
43164
+ ],
43165
+ "requestBody": {
43166
+ "content": {
43167
+ "application/json": {
43168
+ "schema": {
43169
+ "$ref": "#/components/schemas/BreakdownRequest"
43170
+ }
43171
+ }
43172
+ }
43173
+ },
43174
+ "responses": {
43175
+ "200": {
43176
+ "content": {
43177
+ "application/json": {
43178
+ "schema": {
43179
+ "$ref": "#/components/schemas/BreakdownResponse"
43180
+ }
43181
+ }
43182
+ },
43183
+ "description": "Breakdown of the entity's loans by title company"
43184
+ },
43185
+ "400": {
43186
+ "content": {
43187
+ "application/json": {
43188
+ "schema": {
43189
+ "$ref": "#/components/schemas/ErrorResponse"
43190
+ }
43191
+ }
43192
+ },
43193
+ "description": "Validation error"
43194
+ },
43195
+ "401": {
43196
+ "content": {
43197
+ "application/json": {
43198
+ "schema": {
43199
+ "$ref": "#/components/schemas/ErrorResponse"
43200
+ }
43201
+ }
43202
+ },
43203
+ "description": "Unauthorized"
43204
+ },
43205
+ "403": {
43206
+ "content": {
43207
+ "application/json": {
43208
+ "schema": {
43209
+ "$ref": "#/components/schemas/ErrorResponse"
43210
+ }
43211
+ }
43212
+ },
43213
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
43214
+ },
43215
+ "404": {
43216
+ "content": {
43217
+ "application/json": {
43218
+ "schema": {
43219
+ "$ref": "#/components/schemas/ErrorResponse"
43220
+ }
43221
+ }
43222
+ },
43223
+ "description": "Not found"
43224
+ },
43225
+ "413": {
43226
+ "content": {
43227
+ "application/json": {
43228
+ "schema": {
43229
+ "$ref": "#/components/schemas/PayloadTooLarge"
43230
+ }
43231
+ }
43232
+ },
43233
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
43234
+ }
43235
+ },
43236
+ "summary": "Title companies on this company's loans (spellings merged)",
43237
+ "tags": [
43238
+ "Companies"
43239
+ ]
43240
+ }
43241
+ },
42697
43242
  "/v1/companies/{nmlsId}/breakdowns/zip-codes": {
42698
43243
  "post": {
42699
43244
  "operationId": "companyZipCodes",
@@ -43804,27 +44349,222 @@
43804
44349
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
43805
44350
  }
43806
44351
  },
43807
- "summary": "Where a company's hires came from and where its exits went",
44352
+ "summary": "Where a company's hires came from and where its exits went",
44353
+ "tags": [
44354
+ "Companies"
44355
+ ]
44356
+ }
44357
+ },
44358
+ "/v1/crm/activity": {
44359
+ "delete": {
44360
+ "operationId": "deleteCrmNote",
44361
+ "requestBody": {
44362
+ "content": {
44363
+ "application/json": {
44364
+ "schema": {
44365
+ "properties": {
44366
+ "id": {
44367
+ "minLength": 1,
44368
+ "type": "string"
44369
+ }
44370
+ },
44371
+ "required": [
44372
+ "id"
44373
+ ],
44374
+ "type": "object"
44375
+ }
44376
+ }
44377
+ }
44378
+ },
44379
+ "responses": {
44380
+ "200": {
44381
+ "content": {
44382
+ "application/json": {
44383
+ "schema": {
44384
+ "$ref": "#/components/schemas/CrmDeleteNoteResult"
44385
+ }
44386
+ }
44387
+ },
44388
+ "description": "Whether the note was soft-deleted."
44389
+ },
44390
+ "400": {
44391
+ "content": {
44392
+ "application/json": {
44393
+ "schema": {
44394
+ "$ref": "#/components/schemas/ErrorResponse"
44395
+ }
44396
+ }
44397
+ },
44398
+ "description": "Validation error"
44399
+ },
44400
+ "401": {
44401
+ "content": {
44402
+ "application/json": {
44403
+ "schema": {
44404
+ "$ref": "#/components/schemas/ErrorResponse"
44405
+ }
44406
+ }
44407
+ },
44408
+ "description": "Unauthorized"
44409
+ },
44410
+ "403": {
44411
+ "content": {
44412
+ "application/json": {
44413
+ "schema": {
44414
+ "$ref": "#/components/schemas/ErrorResponse"
44415
+ }
44416
+ }
44417
+ },
44418
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
44419
+ },
44420
+ "404": {
44421
+ "content": {
44422
+ "application/json": {
44423
+ "schema": {
44424
+ "$ref": "#/components/schemas/ErrorResponse"
44425
+ }
44426
+ }
44427
+ },
44428
+ "description": "Not found"
44429
+ },
44430
+ "413": {
44431
+ "content": {
44432
+ "application/json": {
44433
+ "schema": {
44434
+ "$ref": "#/components/schemas/PayloadTooLarge"
44435
+ }
44436
+ }
44437
+ },
44438
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
44439
+ }
44440
+ },
44441
+ "summary": "Soft-delete a note",
44442
+ "tags": [
44443
+ "CRM"
44444
+ ]
44445
+ },
44446
+ "get": {
44447
+ "operationId": "listCrmActivity",
44448
+ "parameters": [
44449
+ {
44450
+ "description": "The CRM record id whose timeline to read.",
44451
+ "in": "query",
44452
+ "name": "entityId",
44453
+ "required": true,
44454
+ "schema": {
44455
+ "description": "The CRM record id whose timeline to read.",
44456
+ "minLength": 1,
44457
+ "type": "string"
44458
+ }
44459
+ }
44460
+ ],
44461
+ "responses": {
44462
+ "200": {
44463
+ "content": {
44464
+ "application/json": {
44465
+ "schema": {
44466
+ "$ref": "#/components/schemas/CrmActivityList"
44467
+ }
44468
+ }
44469
+ },
44470
+ "description": "The record's timeline entries, newest first."
44471
+ },
44472
+ "400": {
44473
+ "content": {
44474
+ "application/json": {
44475
+ "schema": {
44476
+ "$ref": "#/components/schemas/ErrorResponse"
44477
+ }
44478
+ }
44479
+ },
44480
+ "description": "Validation error"
44481
+ },
44482
+ "401": {
44483
+ "content": {
44484
+ "application/json": {
44485
+ "schema": {
44486
+ "$ref": "#/components/schemas/ErrorResponse"
44487
+ }
44488
+ }
44489
+ },
44490
+ "description": "Unauthorized"
44491
+ },
44492
+ "403": {
44493
+ "content": {
44494
+ "application/json": {
44495
+ "schema": {
44496
+ "$ref": "#/components/schemas/ErrorResponse"
44497
+ }
44498
+ }
44499
+ },
44500
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
44501
+ },
44502
+ "404": {
44503
+ "content": {
44504
+ "application/json": {
44505
+ "schema": {
44506
+ "$ref": "#/components/schemas/ErrorResponse"
44507
+ }
44508
+ }
44509
+ },
44510
+ "description": "Not found"
44511
+ },
44512
+ "413": {
44513
+ "content": {
44514
+ "application/json": {
44515
+ "schema": {
44516
+ "$ref": "#/components/schemas/PayloadTooLarge"
44517
+ }
44518
+ }
44519
+ },
44520
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
44521
+ }
44522
+ },
44523
+ "summary": "List a record's activity timeline (newest first)",
43808
44524
  "tags": [
43809
- "Companies"
44525
+ "CRM"
43810
44526
  ]
43811
- }
43812
- },
43813
- "/v1/crm/activity": {
43814
- "delete": {
43815
- "operationId": "deleteCrmNote",
44527
+ },
44528
+ "patch": {
44529
+ "operationId": "editCrmNote",
43816
44530
  "requestBody": {
43817
44531
  "content": {
43818
44532
  "application/json": {
43819
44533
  "schema": {
43820
44534
  "properties": {
44535
+ "body": {
44536
+ "type": "string"
44537
+ },
44538
+ "bodyHtml": {
44539
+ "nullable": true,
44540
+ "type": "string"
44541
+ },
44542
+ "grantRecordAccessUserIds": {
44543
+ "items": {
44544
+ "type": "string"
44545
+ },
44546
+ "type": "array"
44547
+ },
43821
44548
  "id": {
43822
44549
  "minLength": 1,
43823
44550
  "type": "string"
44551
+ },
44552
+ "listId": {
44553
+ "nullable": true,
44554
+ "type": "string"
44555
+ },
44556
+ "recordName": {
44557
+ "type": "string"
44558
+ },
44559
+ "recordUrl": {
44560
+ "deprecated": true,
44561
+ "description": "Ignored — the record link is server-built.",
44562
+ "type": "string"
43824
44563
  }
43825
44564
  },
43826
44565
  "required": [
43827
- "id"
44566
+ "id",
44567
+ "body"
43828
44568
  ],
43829
44569
  "type": "object"
43830
44570
  }
@@ -43836,11 +44576,11 @@
43836
44576
  "content": {
43837
44577
  "application/json": {
43838
44578
  "schema": {
43839
- "$ref": "#/components/schemas/CrmDeleteNoteResult"
44579
+ "$ref": "#/components/schemas/CrmEditNoteResult"
43840
44580
  }
43841
44581
  }
43842
44582
  },
43843
- "description": "Whether the note was soft-deleted."
44583
+ "description": "Whether the note was updated, plus its record + prior body."
43844
44584
  },
43845
44585
  "400": {
43846
44586
  "content": {
@@ -43893,36 +44633,75 @@
43893
44633
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
43894
44634
  }
43895
44635
  },
43896
- "summary": "Soft-delete a note",
44636
+ "summary": "Edit a note's body",
43897
44637
  "tags": [
43898
44638
  "CRM"
43899
44639
  ]
43900
44640
  },
43901
- "get": {
43902
- "operationId": "listCrmActivity",
43903
- "parameters": [
43904
- {
43905
- "description": "The CRM record id whose timeline to read.",
43906
- "in": "query",
43907
- "name": "entityId",
43908
- "required": true,
43909
- "schema": {
43910
- "description": "The CRM record id whose timeline to read.",
43911
- "minLength": 1,
43912
- "type": "string"
44641
+ "post": {
44642
+ "operationId": "addCrmNote",
44643
+ "requestBody": {
44644
+ "content": {
44645
+ "application/json": {
44646
+ "schema": {
44647
+ "properties": {
44648
+ "body": {
44649
+ "type": "string"
44650
+ },
44651
+ "bodyHtml": {
44652
+ "nullable": true,
44653
+ "type": "string"
44654
+ },
44655
+ "callId": {
44656
+ "nullable": true,
44657
+ "type": "string"
44658
+ },
44659
+ "entityId": {
44660
+ "minLength": 1,
44661
+ "type": "string"
44662
+ },
44663
+ "grantRecordAccessUserIds": {
44664
+ "items": {
44665
+ "type": "string"
44666
+ },
44667
+ "type": "array"
44668
+ },
44669
+ "listId": {
44670
+ "nullable": true,
44671
+ "type": "string"
44672
+ },
44673
+ "parentId": {
44674
+ "nullable": true,
44675
+ "type": "string"
44676
+ },
44677
+ "recordName": {
44678
+ "type": "string"
44679
+ },
44680
+ "recordUrl": {
44681
+ "deprecated": true,
44682
+ "description": "Ignored — the record link is server-built.",
44683
+ "type": "string"
44684
+ }
44685
+ },
44686
+ "required": [
44687
+ "entityId",
44688
+ "body"
44689
+ ],
44690
+ "type": "object"
44691
+ }
43913
44692
  }
43914
44693
  }
43915
- ],
44694
+ },
43916
44695
  "responses": {
43917
44696
  "200": {
43918
44697
  "content": {
43919
44698
  "application/json": {
43920
44699
  "schema": {
43921
- "$ref": "#/components/schemas/CrmActivityList"
44700
+ "$ref": "#/components/schemas/CrmAddNoteResult"
43922
44701
  }
43923
44702
  }
43924
44703
  },
43925
- "description": "The record's timeline entries, newest first."
44704
+ "description": "The new note's id + its resolved thread root (null if top-level)."
43926
44705
  },
43927
44706
  "400": {
43928
44707
  "content": {
@@ -43975,18 +44754,23 @@
43975
44754
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
43976
44755
  }
43977
44756
  },
43978
- "summary": "List a record's activity timeline (newest first)",
44757
+ "summary": "Append a note to a record's timeline",
43979
44758
  "tags": [
43980
44759
  "CRM"
43981
44760
  ]
43982
- },
44761
+ }
44762
+ },
44763
+ "/v1/crm/activity/log": {
43983
44764
  "patch": {
43984
- "operationId": "editCrmNote",
44765
+ "operationId": "editCrmLoggedActivity",
43985
44766
  "requestBody": {
43986
44767
  "content": {
43987
44768
  "application/json": {
43988
44769
  "schema": {
43989
44770
  "properties": {
44771
+ "activityType": {
44772
+ "$ref": "#/components/schemas/CrmLoggedActivityType"
44773
+ },
43990
44774
  "body": {
43991
44775
  "type": "string"
43992
44776
  },
@@ -43994,6 +44778,23 @@
43994
44778
  "nullable": true,
43995
44779
  "type": "string"
43996
44780
  },
44781
+ "direction": {
44782
+ "description": "call + email only.",
44783
+ "enum": [
44784
+ "inbound",
44785
+ "outbound",
44786
+ null
44787
+ ],
44788
+ "nullable": true,
44789
+ "type": "string"
44790
+ },
44791
+ "durationMinutes": {
44792
+ "description": "call + meeting only, in minutes.",
44793
+ "maximum": 1440,
44794
+ "minimum": 0,
44795
+ "nullable": true,
44796
+ "type": "integer"
44797
+ },
43997
44798
  "grantRecordAccessUserIds": {
43998
44799
  "items": {
43999
44800
  "type": "string"
@@ -44008,17 +44809,31 @@
44008
44809
  "nullable": true,
44009
44810
  "type": "string"
44010
44811
  },
44812
+ "occurredAt": {
44813
+ "description": "Epoch ms the activity actually happened. Must be ≤ now + 5 minutes.",
44814
+ "exclusiveMinimum": true,
44815
+ "minimum": 0,
44816
+ "type": "integer"
44817
+ },
44818
+ "outcome": {
44819
+ "description": "call: connected | voicemail | no_answer | busy. meeting: held | no_show. Not allowed for email/task.",
44820
+ "nullable": true,
44821
+ "type": "string"
44822
+ },
44011
44823
  "recordName": {
44012
44824
  "type": "string"
44013
44825
  },
44014
- "recordUrl": {
44015
- "deprecated": true,
44016
- "description": "Ignored — the record link is server-built.",
44826
+ "title": {
44827
+ "description": "Meeting title / email subject / task title. Not allowed for a call; required for a task.",
44828
+ "maxLength": 200,
44829
+ "nullable": true,
44017
44830
  "type": "string"
44018
44831
  }
44019
44832
  },
44020
44833
  "required": [
44021
44834
  "id",
44835
+ "activityType",
44836
+ "occurredAt",
44022
44837
  "body"
44023
44838
  ],
44024
44839
  "type": "object"
@@ -44031,11 +44846,11 @@
44031
44846
  "content": {
44032
44847
  "application/json": {
44033
44848
  "schema": {
44034
- "$ref": "#/components/schemas/CrmEditNoteResult"
44849
+ "$ref": "#/components/schemas/CrmEditLoggedActivityResult"
44035
44850
  }
44036
44851
  }
44037
44852
  },
44038
- "description": "Whether the note was updated, plus its record + prior body."
44853
+ "description": "Whether the logged entry was updated."
44039
44854
  },
44040
44855
  "400": {
44041
44856
  "content": {
@@ -44075,7 +44890,7 @@
44075
44890
  }
44076
44891
  }
44077
44892
  },
44078
- "description": "Not found"
44893
+ "description": "No live logged entry with that id — either it doesn't exist, was deleted, or isn't a logged entry (this route can't edit a plain note)."
44079
44894
  },
44080
44895
  "413": {
44081
44896
  "content": {
@@ -44088,18 +44903,21 @@
44088
44903
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
44089
44904
  }
44090
44905
  },
44091
- "summary": "Edit a note's body",
44906
+ "summary": "Edit a logged activity entry",
44092
44907
  "tags": [
44093
44908
  "CRM"
44094
44909
  ]
44095
44910
  },
44096
44911
  "post": {
44097
- "operationId": "addCrmNote",
44912
+ "operationId": "logCrmActivity",
44098
44913
  "requestBody": {
44099
44914
  "content": {
44100
44915
  "application/json": {
44101
44916
  "schema": {
44102
44917
  "properties": {
44918
+ "activityType": {
44919
+ "$ref": "#/components/schemas/CrmLoggedActivityType"
44920
+ },
44103
44921
  "body": {
44104
44922
  "type": "string"
44105
44923
  },
@@ -44107,10 +44925,23 @@
44107
44925
  "nullable": true,
44108
44926
  "type": "string"
44109
44927
  },
44110
- "callId": {
44928
+ "direction": {
44929
+ "description": "call + email only.",
44930
+ "enum": [
44931
+ "inbound",
44932
+ "outbound",
44933
+ null
44934
+ ],
44111
44935
  "nullable": true,
44112
44936
  "type": "string"
44113
44937
  },
44938
+ "durationMinutes": {
44939
+ "description": "call + meeting only, in minutes.",
44940
+ "maximum": 1440,
44941
+ "minimum": 0,
44942
+ "nullable": true,
44943
+ "type": "integer"
44944
+ },
44114
44945
  "entityId": {
44115
44946
  "minLength": 1,
44116
44947
  "type": "string"
@@ -44125,21 +44956,31 @@
44125
44956
  "nullable": true,
44126
44957
  "type": "string"
44127
44958
  },
44128
- "parentId": {
44959
+ "occurredAt": {
44960
+ "description": "Epoch ms the activity actually happened. Must be ≤ now + 5 minutes.",
44961
+ "exclusiveMinimum": true,
44962
+ "minimum": 0,
44963
+ "type": "integer"
44964
+ },
44965
+ "outcome": {
44966
+ "description": "call: connected | voicemail | no_answer | busy. meeting: held | no_show. Not allowed for email/task.",
44129
44967
  "nullable": true,
44130
44968
  "type": "string"
44131
44969
  },
44132
44970
  "recordName": {
44133
44971
  "type": "string"
44134
44972
  },
44135
- "recordUrl": {
44136
- "deprecated": true,
44137
- "description": "Ignored — the record link is server-built.",
44973
+ "title": {
44974
+ "description": "Meeting title / email subject / task title. Not allowed for a call; required for a task.",
44975
+ "maxLength": 200,
44976
+ "nullable": true,
44138
44977
  "type": "string"
44139
44978
  }
44140
44979
  },
44141
44980
  "required": [
44142
44981
  "entityId",
44982
+ "activityType",
44983
+ "occurredAt",
44143
44984
  "body"
44144
44985
  ],
44145
44986
  "type": "object"
@@ -44152,11 +44993,11 @@
44152
44993
  "content": {
44153
44994
  "application/json": {
44154
44995
  "schema": {
44155
- "$ref": "#/components/schemas/CrmAddNoteResult"
44996
+ "$ref": "#/components/schemas/CrmLogActivityResult"
44156
44997
  }
44157
44998
  }
44158
44999
  },
44159
- "description": "The new note's id + its resolved thread root (null if top-level)."
45000
+ "description": "The new logged entry's id."
44160
45001
  },
44161
45002
  "400": {
44162
45003
  "content": {
@@ -44209,7 +45050,7 @@
44209
45050
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
44210
45051
  }
44211
45052
  },
44212
- "summary": "Append a note to a record's timeline",
45053
+ "summary": "Log a Call/Meeting/Email/Task that happened outside Model Match",
44213
45054
  "tags": [
44214
45055
  "CRM"
44215
45056
  ]
@@ -61721,6 +62562,99 @@
61721
62562
  ]
61722
62563
  }
61723
62564
  },
62565
+ "/v1/originators/{nmlsId}/breakdowns/title-companies": {
62566
+ "post": {
62567
+ "description": "Ranks the title companies on this entity's loans (recorded deeds, `recording_date` window — the same population and period axis as every other loans breakdown). Spellings of one company are MERGED: `label` is its most-used spelling, `id` the normalized name every spelling was grouped under (legal-form and filler words such as `inc`, `llc`, `co`, `company`, `title`, `escrow` removed; `natl` → `national`). Placeholder entries meaning \"no title company recorded\" (`none available` and its misspellings, `unknown`, `n/a`, …) and names shorter than 3 characters are excluded rather than ranked. `attorney` and `accommodation` are kept: they say who closed. `pctUnits` / `pctVolume` are shares of the entity's WHOLE loan population in the window, including loans with no title company, so they do not sum to 100 across the page. Pages of up to 100 via `pagination`.",
62568
+ "operationId": "originatorTitleCompanies",
62569
+ "parameters": [
62570
+ {
62571
+ "description": "Originator NMLS ID",
62572
+ "in": "path",
62573
+ "name": "nmlsId",
62574
+ "required": true,
62575
+ "schema": {
62576
+ "description": "Originator NMLS ID",
62577
+ "type": "string"
62578
+ }
62579
+ }
62580
+ ],
62581
+ "requestBody": {
62582
+ "content": {
62583
+ "application/json": {
62584
+ "schema": {
62585
+ "$ref": "#/components/schemas/BreakdownRequest"
62586
+ }
62587
+ }
62588
+ }
62589
+ },
62590
+ "responses": {
62591
+ "200": {
62592
+ "content": {
62593
+ "application/json": {
62594
+ "schema": {
62595
+ "$ref": "#/components/schemas/BreakdownResponse"
62596
+ }
62597
+ }
62598
+ },
62599
+ "description": "Breakdown of the entity's loans by title company"
62600
+ },
62601
+ "400": {
62602
+ "content": {
62603
+ "application/json": {
62604
+ "schema": {
62605
+ "$ref": "#/components/schemas/ErrorResponse"
62606
+ }
62607
+ }
62608
+ },
62609
+ "description": "Validation error"
62610
+ },
62611
+ "401": {
62612
+ "content": {
62613
+ "application/json": {
62614
+ "schema": {
62615
+ "$ref": "#/components/schemas/ErrorResponse"
62616
+ }
62617
+ }
62618
+ },
62619
+ "description": "Unauthorized"
62620
+ },
62621
+ "403": {
62622
+ "content": {
62623
+ "application/json": {
62624
+ "schema": {
62625
+ "$ref": "#/components/schemas/ErrorResponse"
62626
+ }
62627
+ }
62628
+ },
62629
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
62630
+ },
62631
+ "404": {
62632
+ "content": {
62633
+ "application/json": {
62634
+ "schema": {
62635
+ "$ref": "#/components/schemas/ErrorResponse"
62636
+ }
62637
+ }
62638
+ },
62639
+ "description": "Not found"
62640
+ },
62641
+ "413": {
62642
+ "content": {
62643
+ "application/json": {
62644
+ "schema": {
62645
+ "$ref": "#/components/schemas/PayloadTooLarge"
62646
+ }
62647
+ }
62648
+ },
62649
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
62650
+ }
62651
+ },
62652
+ "summary": "Title companies on this LO's loans (spellings merged)",
62653
+ "tags": [
62654
+ "Originators"
62655
+ ]
62656
+ }
62657
+ },
61724
62658
  "/v1/originators/{nmlsId}/breakdowns/zip-codes": {
61725
62659
  "post": {
61726
62660
  "operationId": "originatorZipCodes",