@model-match/api 3.14.0 → 3.16.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
@@ -1144,13 +1144,13 @@
1144
1144
  "AgentCountyFilterValue": {
1145
1145
  "anyOf": [
1146
1146
  {
1147
- "pattern": "^\\d{5}$/u",
1147
+ "pattern": "^\\d{5}$",
1148
1148
  "type": "string"
1149
1149
  },
1150
1150
  {
1151
1151
  "description": "Match any of several counties",
1152
1152
  "items": {
1153
- "pattern": "^\\d{5}$/u",
1153
+ "pattern": "^\\d{5}$",
1154
1154
  "type": "string"
1155
1155
  },
1156
1156
  "type": "array"
@@ -1969,13 +1969,13 @@
1969
1969
  "from": {
1970
1970
  "description": "Inclusive lower bound on the transaction date.",
1971
1971
  "example": "2024-01-01",
1972
- "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$/u",
1972
+ "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$",
1973
1973
  "type": "string"
1974
1974
  },
1975
1975
  "to": {
1976
1976
  "description": "Inclusive upper bound on the transaction date.",
1977
1977
  "example": "2025-12-31",
1978
- "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$/u",
1978
+ "pattern": "^\\d{4}(-\\d{2}(-\\d{2})?)?$",
1979
1979
  "type": "string"
1980
1980
  }
1981
1981
  },
@@ -3102,7 +3102,7 @@
3102
3102
  "$ref": "#/components/schemas/NumericFilterValue"
3103
3103
  },
3104
3104
  {
3105
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
3105
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
3106
3106
  }
3107
3107
  ]
3108
3108
  },
@@ -4518,7 +4518,7 @@
4518
4518
  "$ref": "#/components/schemas/NumericFilterValue"
4519
4519
  },
4520
4520
  {
4521
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
4521
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
4522
4522
  }
4523
4523
  ]
4524
4524
  },
@@ -4827,7 +4827,7 @@
4827
4827
  "$ref": "#/components/schemas/NumericFilterValue"
4828
4828
  },
4829
4829
  {
4830
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
4830
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
4831
4831
  }
4832
4832
  ]
4833
4833
  },
@@ -5189,7 +5189,7 @@
5189
5189
  "$ref": "#/components/schemas/NumericFilterValue"
5190
5190
  },
5191
5191
  {
5192
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
5192
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
5193
5193
  }
5194
5194
  ]
5195
5195
  },
@@ -5269,7 +5269,7 @@
5269
5269
  "$ref": "#/components/schemas/Pagination"
5270
5270
  },
5271
5271
  "period": {
5272
- "$ref": "#/components/schemas/Period"
5272
+ "$ref": "#/components/schemas/DatedPeriod"
5273
5273
  },
5274
5274
  "sort": {
5275
5275
  "$ref": "#/components/schemas/BreakdownSort"
@@ -5644,6 +5644,10 @@
5644
5644
  "measure": {
5645
5645
  "type": "string"
5646
5646
  },
5647
+ "nextCursor": {
5648
+ "description": "Paged party slices only (a chart request that passed `size` or `after` with `slice` `originator` / `broker` / `loanCompany`). Opaque cursor for the next page of the ranking — send it back as `after` with the same body. Absent on the last page.",
5649
+ "type": "string"
5650
+ },
5647
5651
  "omittedCount": {
5648
5652
  "description": "How many documents fell outside the bars — the size of the dropped tail, not its value. A chart groups by a capped number of distinct values, so a slice with more of them than the cap returns its largest groups and drops the rest; `zip`, `city`, `lender` and `originator` are the ones that reach it. It is the documents in the groups past the size cap, and only those: documents excluded from the slice by its empty-value filter, or carrying no value for it at all, are never grouped and are not counted here either. Where the response carries an `unattributed` row that row collects them; elsewhere a small residual against `/analytics/summary` can remain on `city`, `lender`, `originator` and similar, so bars plus this count reconcile with the summary's population exactly only on a slice with no such documents. `0` means the bars ARE the complete set, and the field is absent only where the grouping cannot truncate at all — so `0` and missing are different claims. Unlike a breakdown there is no next page to fetch: narrow the filters or pick a coarser slice. It is a DOCUMENT count and never a dollar or an average — the aggregation reports how many rows it left out, not what they were worth, so a truncated volume chart cannot be completed from this figure. One exception it does not cover: the average measures drop buckets thinner than a minimum sample size, and those are not counted here — compare against the summary when a mean matters.",
5649
5653
  "type": "number"
@@ -5654,6 +5658,14 @@
5654
5658
  },
5655
5659
  "slice": {
5656
5660
  "type": "string"
5661
+ },
5662
+ "total": {
5663
+ "description": "Paged party slices only. Distinct bucket keys (e.g. originators) in the filtered population — what a consumer paginates against. A cardinality ESTIMATE (see `totalApproximate`): near-exact below 40,000, approximate above. On an average measure it can exceed the ranked buckets, which drop keys under the 5-document floor. Use `nextCursor`, not `total`, to decide whether another page exists.",
5664
+ "type": "integer"
5665
+ },
5666
+ "totalApproximate": {
5667
+ "description": "Present (always `true`) alongside `total`: the total is a cardinality estimate, not an exact count.",
5668
+ "type": "boolean"
5657
5669
  }
5658
5670
  },
5659
5671
  "required": [
@@ -6028,7 +6040,7 @@
6028
6040
  "$ref": "#/components/schemas/NumericFilterValue"
6029
6041
  },
6030
6042
  {
6031
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
6043
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
6032
6044
  }
6033
6045
  ]
6034
6046
  },
@@ -6411,7 +6423,7 @@
6411
6423
  "$ref": "#/components/schemas/NumericFilterValue"
6412
6424
  },
6413
6425
  {
6414
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
6426
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
6415
6427
  }
6416
6428
  ]
6417
6429
  },
@@ -8412,7 +8424,7 @@
8412
8424
  "$ref": "#/components/schemas/NumericFilterValue"
8413
8425
  },
8414
8426
  {
8415
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
8427
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
8416
8428
  }
8417
8429
  ]
8418
8430
  },
@@ -9118,7 +9130,7 @@
9118
9130
  "$ref": "#/components/schemas/NumericFilterValue"
9119
9131
  },
9120
9132
  {
9121
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
9133
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
9122
9134
  }
9123
9135
  ]
9124
9136
  },
@@ -9815,7 +9827,7 @@
9815
9827
  "$ref": "#/components/schemas/NumericFilterValue"
9816
9828
  },
9817
9829
  {
9818
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
9830
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
9819
9831
  }
9820
9832
  ]
9821
9833
  },
@@ -10201,7 +10213,7 @@
10201
10213
  "$ref": "#/components/schemas/NumericFilterValue"
10202
10214
  },
10203
10215
  {
10204
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
10216
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
10205
10217
  }
10206
10218
  ]
10207
10219
  },
@@ -10380,6 +10392,98 @@
10380
10392
  ],
10381
10393
  "type": "object"
10382
10394
  },
10395
+ "CreateSavedFilterRequest": {
10396
+ "additionalProperties": false,
10397
+ "properties": {
10398
+ "fidelity": {
10399
+ "description": "How much of the UI state `query` captures: `exact`; `partial` (see `unmapped`); `ui_only` — no runnable query. Defaults from whether `query` is present.",
10400
+ "enum": [
10401
+ "exact",
10402
+ "partial",
10403
+ "ui_only"
10404
+ ],
10405
+ "type": "string"
10406
+ },
10407
+ "icon": {
10408
+ "$ref": "#/components/schemas/SavedFilterIcon"
10409
+ },
10410
+ "kind": {
10411
+ "description": "`filter` (default) — a saved search; `view` — a saved CRM table layout (columns / order) kept in `uiState`. Views never carry a `query` and have their own 25-per-target quota. Fixed at create.",
10412
+ "enum": [
10413
+ "filter",
10414
+ "view"
10415
+ ],
10416
+ "type": "string"
10417
+ },
10418
+ "name": {
10419
+ "maxLength": 100,
10420
+ "minLength": 1,
10421
+ "type": "string"
10422
+ },
10423
+ "query": {
10424
+ "additionalProperties": {
10425
+ "nullable": true
10426
+ },
10427
+ "description": "The target list operation's request body WITHOUT `pagination` (e.g. `{ flatFilters, filters, sort, period }` for listOriginators). Validated against that operation's schema; unknown filter fields are rejected. Null/omitted for a UI-only filter.",
10428
+ "nullable": true,
10429
+ "type": "object"
10430
+ },
10431
+ "target": {
10432
+ "description": "Which list the filter runs against. Market targets run against that entity's list operation (see `operationId`, e.g. listOriginators); `crm_people` / `crm_companies` are UI-only (filtered client-side).",
10433
+ "enum": [
10434
+ "originators",
10435
+ "agents",
10436
+ "companies",
10437
+ "branches",
10438
+ "offices",
10439
+ "loans",
10440
+ "properties",
10441
+ "sales",
10442
+ "crm_people",
10443
+ "crm_companies"
10444
+ ],
10445
+ "type": "string"
10446
+ },
10447
+ "uiState": {
10448
+ "additionalProperties": {
10449
+ "nullable": true
10450
+ },
10451
+ "description": "Opaque client UI state used to restore a filter panel (≤16KB). Not interpreted by the API.",
10452
+ "nullable": true,
10453
+ "type": "object"
10454
+ },
10455
+ "uiStateVersion": {
10456
+ "minimum": 1,
10457
+ "type": "integer"
10458
+ },
10459
+ "unmapped": {
10460
+ "description": "UI filter keys the client could not express in `query` (set with `fidelity: partial`).",
10461
+ "items": {
10462
+ "maxLength": 200,
10463
+ "minLength": 1,
10464
+ "type": "string"
10465
+ },
10466
+ "maxItems": 100,
10467
+ "type": "array"
10468
+ }
10469
+ },
10470
+ "required": [
10471
+ "name",
10472
+ "target"
10473
+ ],
10474
+ "type": "object"
10475
+ },
10476
+ "CreateSavedFilterResult": {
10477
+ "properties": {
10478
+ "data": {
10479
+ "$ref": "#/components/schemas/SavedFilter"
10480
+ }
10481
+ },
10482
+ "required": [
10483
+ "data"
10484
+ ],
10485
+ "type": "object"
10486
+ },
10383
10487
  "CrmActiveCall": {
10384
10488
  "nullable": true,
10385
10489
  "properties": {
@@ -16075,6 +16179,58 @@
16075
16179
  }
16076
16180
  ]
16077
16181
  },
16182
+ "DatedPeriod": {
16183
+ "description": "Date window for loans / sales / market endpoints. Accepts every `Period` value plus rolling windows: `last30Days` / `last60Days` / `last90Days` (today minus 30 / 60 / 90 days through today), `previousMonth` (the last complete calendar month) and `currentMonth` (the 1st of this month through today). All windows are resolved in UTC with inclusive calendar-date bounds. Rolling windows are only accepted by the loans, sales and market endpoints (list, count, breakdowns, analytics summary / time-series / chart); originators / companies / branches / agents / offices endpoints return 400 for them.",
16184
+ "enum": [
16185
+ "2017",
16186
+ "2018",
16187
+ "2019",
16188
+ "2020",
16189
+ "2021",
16190
+ "2022",
16191
+ "2023",
16192
+ "2024",
16193
+ "2025",
16194
+ "2026",
16195
+ "last3Months",
16196
+ "last6Months",
16197
+ "last12Months",
16198
+ "last14Months",
16199
+ "last16Months",
16200
+ "last18Months",
16201
+ "last24Months",
16202
+ "yearToDate",
16203
+ "allTime",
16204
+ "last30Days",
16205
+ "last60Days",
16206
+ "last90Days",
16207
+ "previousMonth",
16208
+ "currentMonth"
16209
+ ],
16210
+ "type": "string"
16211
+ },
16212
+ "DeleteSavedFilterResult": {
16213
+ "properties": {
16214
+ "data": {
16215
+ "properties": {
16216
+ "deleted": {
16217
+ "enum": [
16218
+ true
16219
+ ],
16220
+ "type": "boolean"
16221
+ }
16222
+ },
16223
+ "required": [
16224
+ "deleted"
16225
+ ],
16226
+ "type": "object"
16227
+ }
16228
+ },
16229
+ "required": [
16230
+ "data"
16231
+ ],
16232
+ "type": "object"
16233
+ },
16078
16234
  "EarlyExitCounterparty": {
16079
16235
  "properties": {
16080
16236
  "name": {
@@ -16854,14 +17010,130 @@
16854
17010
  ],
16855
17011
  "type": "object"
16856
17012
  },
17013
+ "EnrichmentCacheStats": {
17014
+ "properties": {
17015
+ "computedAt": {
17016
+ "description": "ISO 8601 timestamp",
17017
+ "type": "string"
17018
+ },
17019
+ "costUsd": {
17020
+ "description": "`total × unitCostUsd` — upstream spend behind the live table.",
17021
+ "minimum": 0,
17022
+ "type": "number"
17023
+ },
17024
+ "empty": {
17025
+ "description": "Unexpired rows where the provider returned no persons.",
17026
+ "minimum": 0,
17027
+ "type": "integer"
17028
+ },
17029
+ "expired": {
17030
+ "minimum": 0,
17031
+ "type": "integer"
17032
+ },
17033
+ "healthy": {
17034
+ "description": "Unexpired rows with at least one person.",
17035
+ "minimum": 0,
17036
+ "type": "integer"
17037
+ },
17038
+ "memberRows": {
17039
+ "description": "Member entitlement rows across every workspace.",
17040
+ "minimum": 0,
17041
+ "type": "integer"
17042
+ },
17043
+ "partial": {
17044
+ "description": "True when the scan hit its page/time budget; counts are a lower bound.",
17045
+ "type": "boolean"
17046
+ },
17047
+ "savedLookups": {
17048
+ "description": "Member entitlements served from the internal cache rather than a fresh BatchData call (every member beyond the first per property).",
17049
+ "minimum": 0,
17050
+ "type": "integer"
17051
+ },
17052
+ "savingsUsd": {
17053
+ "description": "`savedLookups × unitCostUsd`.",
17054
+ "minimum": 0,
17055
+ "type": "number"
17056
+ },
17057
+ "total": {
17058
+ "description": "Internal property cache rows — one per enriched property.",
17059
+ "minimum": 0,
17060
+ "type": "integer"
17061
+ },
17062
+ "unitCostUsd": {
17063
+ "minimum": 0,
17064
+ "type": "number"
17065
+ }
17066
+ },
17067
+ "required": [
17068
+ "total",
17069
+ "expired",
17070
+ "empty",
17071
+ "healthy",
17072
+ "memberRows",
17073
+ "savedLookups",
17074
+ "costUsd",
17075
+ "savingsUsd",
17076
+ "unitCostUsd",
17077
+ "partial",
17078
+ "computedAt"
17079
+ ],
17080
+ "type": "object"
17081
+ },
17082
+ "EnrichmentCharge": {
17083
+ "properties": {
17084
+ "at": {
17085
+ "description": "ISO 8601 time of the debit",
17086
+ "type": "string"
17087
+ },
17088
+ "credits": {
17089
+ "type": "number"
17090
+ },
17091
+ "refresh": {
17092
+ "description": "True when this charge came from a forced re-run",
17093
+ "type": "boolean"
17094
+ }
17095
+ },
17096
+ "required": [
17097
+ "at",
17098
+ "credits",
17099
+ "refresh"
17100
+ ],
17101
+ "type": "object"
17102
+ },
16857
17103
  "EnrichmentListItem": {
16858
17104
  "properties": {
17105
+ "charged": {
17106
+ "description": "Whether the call that produced the current result debited credits. `null` for results stored before charges were recorded.",
17107
+ "nullable": true,
17108
+ "type": "boolean"
17109
+ },
17110
+ "charges": {
17111
+ "description": "Every recorded debit for this property, oldest first. More than one means it was paid for more than once (a `refresh` re-run). `null` when not recorded.",
17112
+ "items": {
17113
+ "$ref": "#/components/schemas/EnrichmentCharge"
17114
+ },
17115
+ "nullable": true,
17116
+ "type": "array"
17117
+ },
17118
+ "creditsCharged": {
17119
+ "description": "Total credits debited for this property across every recorded run, including forced re-runs. `null` when not recorded.",
17120
+ "nullable": true,
17121
+ "type": "number"
17122
+ },
17123
+ "emailCount": {
17124
+ "description": "Email addresses found across every matched person.",
17125
+ "type": "number"
17126
+ },
16859
17127
  "enrichedAt": {
16860
17128
  "type": "string"
16861
17129
  },
16862
17130
  "personCount": {
16863
17131
  "type": "number"
16864
17132
  },
17133
+ "phoneCount": {
17134
+ "description": "Phone numbers found across every matched person. A matched person can have none.",
17135
+ "type": "number"
17136
+ },
16865
17137
  "propertyId": {
16866
17138
  "type": "string"
16867
17139
  },
@@ -16873,7 +17145,12 @@
16873
17145
  "propertyId",
16874
17146
  "enrichedAt",
16875
17147
  "source",
16876
- "personCount"
17148
+ "personCount",
17149
+ "phoneCount",
17150
+ "emailCount",
17151
+ "charged",
17152
+ "creditsCharged",
17153
+ "charges"
16877
17154
  ],
16878
17155
  "type": "object"
16879
17156
  },
@@ -17700,6 +17977,17 @@
17700
17977
  ],
17701
17978
  "type": "object"
17702
17979
  },
17980
+ "GetSavedFilterResult": {
17981
+ "properties": {
17982
+ "data": {
17983
+ "$ref": "#/components/schemas/SavedFilter"
17984
+ }
17985
+ },
17986
+ "required": [
17987
+ "data"
17988
+ ],
17989
+ "type": "object"
17990
+ },
17703
17991
  "HeadcountPoint": {
17704
17992
  "properties": {
17705
17993
  "active": {
@@ -18377,6 +18665,11 @@
18377
18665
  "advancedFilters": {
18378
18666
  "$ref": "#/components/schemas/FilterNode"
18379
18667
  },
18668
+ "after": {
18669
+ "description": "PARTY slices only. Opaque cursor from a previous page's `nextCursor`; send the same body otherwise. A cursor is bound to the `slice` and `measure` it was issued for (400 if they change). It is an offset into the ranking, so a rolling `period` re-resolved on a later UTC day ranks a shifted window.",
18670
+ "minLength": 1,
18671
+ "type": "string"
18672
+ },
18380
18673
  "filters": {
18381
18674
  "$ref": "#/components/schemas/FilterNode"
18382
18675
  },
@@ -18590,7 +18883,7 @@
18590
18883
  "$ref": "#/components/schemas/NumericFilterValue"
18591
18884
  },
18592
18885
  {
18593
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
18886
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
18594
18887
  }
18595
18888
  ]
18596
18889
  },
@@ -18670,7 +18963,13 @@
18670
18963
  "$ref": "#/components/schemas/ChartMeasure"
18671
18964
  },
18672
18965
  "period": {
18673
- "$ref": "#/components/schemas/Period"
18966
+ "$ref": "#/components/schemas/DatedPeriod"
18967
+ },
18968
+ "size": {
18969
+ "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
+ "maximum": 1000,
18971
+ "minimum": 1,
18972
+ "type": "integer"
18674
18973
  },
18675
18974
  "slice": {
18676
18975
  "$ref": "#/components/schemas/LoanSlice"
@@ -18988,7 +19287,7 @@
18988
19287
  "$ref": "#/components/schemas/NumericFilterValue"
18989
19288
  },
18990
19289
  {
18991
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
19290
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
18992
19291
  }
18993
19292
  ]
18994
19293
  },
@@ -19068,7 +19367,7 @@
19068
19367
  "$ref": "#/components/schemas/Pagination"
19069
19368
  },
19070
19369
  "period": {
19071
- "$ref": "#/components/schemas/Period"
19370
+ "$ref": "#/components/schemas/DatedPeriod"
19072
19371
  },
19073
19372
  "sort": {
19074
19373
  "items": {
@@ -19534,7 +19833,7 @@
19534
19833
  "$ref": "#/components/schemas/NumericFilterValue"
19535
19834
  },
19536
19835
  {
19537
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
19836
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
19538
19837
  }
19539
19838
  ]
19540
19839
  },
@@ -19611,7 +19910,7 @@
19611
19910
  "type": "object"
19612
19911
  },
19613
19912
  "period": {
19614
- "$ref": "#/components/schemas/Period"
19913
+ "$ref": "#/components/schemas/DatedPeriod"
19615
19914
  }
19616
19915
  },
19617
19916
  "type": "object"
@@ -19835,7 +20134,7 @@
19835
20134
  "$ref": "#/components/schemas/NumericFilterValue"
19836
20135
  },
19837
20136
  {
19838
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
20137
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
19839
20138
  }
19840
20139
  ]
19841
20140
  },
@@ -19918,7 +20217,7 @@
19918
20217
  "$ref": "#/components/schemas/Measure"
19919
20218
  },
19920
20219
  "period": {
19921
- "$ref": "#/components/schemas/Period"
20220
+ "$ref": "#/components/schemas/DatedPeriod"
19922
20221
  },
19923
20222
  "segment": {
19924
20223
  "$ref": "#/components/schemas/LoanSlice"
@@ -20221,7 +20520,7 @@
20221
20520
  "$ref": "#/components/schemas/MarketChartMeasure"
20222
20521
  },
20223
20522
  "period": {
20224
- "$ref": "#/components/schemas/Period"
20523
+ "$ref": "#/components/schemas/DatedPeriod"
20225
20524
  },
20226
20525
  "slice": {
20227
20526
  "$ref": "#/components/schemas/MarketSliceDimension"
@@ -20413,7 +20712,7 @@
20413
20712
  "type": "integer"
20414
20713
  },
20415
20714
  "period": {
20416
- "$ref": "#/components/schemas/Period"
20715
+ "$ref": "#/components/schemas/DatedPeriod"
20417
20716
  },
20418
20717
  "slice": {
20419
20718
  "description": "Break the mix down by a market dimension — `metro` for the metro (CBSA) grain, `state` / `county` / `zip` for geography, or any product dimension. Each bucket reports its OWN denominators, so bucket shares are comparable to each other and to the overall mix. Omit for the overall mix only.",
@@ -20839,7 +21138,7 @@
20839
21138
  "$ref": "#/components/schemas/Pagination"
20840
21139
  },
20841
21140
  "period": {
20842
- "$ref": "#/components/schemas/Period"
21141
+ "$ref": "#/components/schemas/DatedPeriod"
20843
21142
  },
20844
21143
  "sort": {
20845
21144
  "items": {
@@ -21198,7 +21497,7 @@
21198
21497
  "$ref": "#/components/schemas/MarketFlatFilters"
21199
21498
  },
21200
21499
  "period": {
21201
- "$ref": "#/components/schemas/Period"
21500
+ "$ref": "#/components/schemas/DatedPeriod"
21202
21501
  }
21203
21502
  },
21204
21503
  "type": "object"
@@ -21291,7 +21590,7 @@
21291
21590
  "$ref": "#/components/schemas/MarketMeasure"
21292
21591
  },
21293
21592
  "period": {
21294
- "$ref": "#/components/schemas/Period"
21593
+ "$ref": "#/components/schemas/DatedPeriod"
21295
21594
  },
21296
21595
  "segment": {
21297
21596
  "$ref": "#/components/schemas/MarketSliceDimension"
@@ -23426,7 +23725,7 @@
23426
23725
  "$ref": "#/components/schemas/NumericFilterValue"
23427
23726
  },
23428
23727
  {
23429
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
23728
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
23430
23729
  }
23431
23730
  ]
23432
23731
  },
@@ -25497,11 +25796,11 @@
25497
25796
  "description": "Custom window. A period is a fixed pre-aggregated window and cannot be re-cut, so any bound here switches the response to live transaction data.",
25498
25797
  "properties": {
25499
25798
  "from": {
25500
- "pattern": "^\\d{4}(?:-\\d{2}(?:-\\d{2})?)?$/u",
25799
+ "pattern": "^\\d{4}(?:-\\d{2}(?:-\\d{2})?)?$",
25501
25800
  "type": "string"
25502
25801
  },
25503
25802
  "to": {
25504
- "pattern": "^\\d{4}(?:-\\d{2}(?:-\\d{2})?)?$/u",
25803
+ "pattern": "^\\d{4}(?:-\\d{2}(?:-\\d{2})?)?$",
25505
25804
  "type": "string"
25506
25805
  }
25507
25806
  },
@@ -26376,7 +26675,7 @@
26376
26675
  "$ref": "#/components/schemas/NumericFilterValue"
26377
26676
  },
26378
26677
  {
26379
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
26678
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
26380
26679
  }
26381
26680
  ]
26382
26681
  },
@@ -26686,7 +26985,7 @@
26686
26985
  "$ref": "#/components/schemas/NumericFilterValue"
26687
26986
  },
26688
26987
  {
26689
- "description": "Loan-to-value at ORIGINATION (sparse). This is not a current LTV — there is no stored current-LTV field on loans."
26988
+ "description": "Loan-to-value at ORIGINATION (sparse), as a PERCENT on a 0-100 scale (`80` = 80% LTV; values above 100 are real underwater loans). This is not a current LTV — there is no stored current-LTV field on loans."
26690
26989
  }
26691
26990
  ]
26692
26991
  },
@@ -26867,6 +27166,7 @@
26867
27166
  "type": "object"
26868
27167
  },
26869
27168
  "Period": {
27169
+ "description": "Pre-aggregated period block. Rolling windows (last30Days, last60Days, last90Days, previousMonth, currentMonth) are NOT accepted here and return 400; they are supported on the loans, sales and market endpoints (list, count, breakdowns, analytics summary / time-series / chart) (see `DatedPeriod`).",
26870
27170
  "enum": [
26871
27171
  "2017",
26872
27172
  "2018",
@@ -29711,7 +30011,7 @@
29711
30011
  "properties": {
29712
30012
  "asOf": {
29713
30013
  "description": "The subscribe day (yyyy-MM-dd). Step 0 replays the backfill window a real subscribe would deliver on this date.",
29714
- "pattern": "^\\d{4}-\\d{2}-\\d{2}/u",
30014
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}",
29715
30015
  "type": "string"
29716
30016
  },
29717
30017
  "backfillDays": {
@@ -29741,7 +30041,7 @@
29741
30041
  "steps": {
29742
30042
  "description": "Later dates (yyyy-MM-dd) to advance to. Each shows only what is NEW since the previous date — the forward delta the cron would have delivered. Sorted + de-duplicated; capped at 12 dates total.",
29743
30043
  "items": {
29744
- "pattern": "^\\d{4}-\\d{2}-\\d{2}/u",
30044
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}",
29745
30045
  "type": "string"
29746
30046
  },
29747
30047
  "type": "array"
@@ -29986,7 +30286,7 @@
29986
30286
  "$ref": "#/components/schemas/ChartMeasure"
29987
30287
  },
29988
30288
  "period": {
29989
- "$ref": "#/components/schemas/Period"
30289
+ "$ref": "#/components/schemas/DatedPeriod"
29990
30290
  },
29991
30291
  "slice": {
29992
30292
  "$ref": "#/components/schemas/SaleChartSlice"
@@ -30186,7 +30486,7 @@
30186
30486
  "$ref": "#/components/schemas/Pagination"
30187
30487
  },
30188
30488
  "period": {
30189
- "$ref": "#/components/schemas/Period"
30489
+ "$ref": "#/components/schemas/DatedPeriod"
30190
30490
  },
30191
30491
  "sort": {
30192
30492
  "items": {
@@ -30440,7 +30740,7 @@
30440
30740
  "$ref": "#/components/schemas/SaleFlatFilters"
30441
30741
  },
30442
30742
  "period": {
30443
- "$ref": "#/components/schemas/Period"
30743
+ "$ref": "#/components/schemas/DatedPeriod"
30444
30744
  }
30445
30745
  },
30446
30746
  "type": "object"
@@ -30464,7 +30764,7 @@
30464
30764
  "$ref": "#/components/schemas/Measure"
30465
30765
  },
30466
30766
  "period": {
30467
- "$ref": "#/components/schemas/Period"
30767
+ "$ref": "#/components/schemas/DatedPeriod"
30468
30768
  },
30469
30769
  "segment": {
30470
30770
  "$ref": "#/components/schemas/SegmentDimension"
@@ -30547,6 +30847,260 @@
30547
30847
  ],
30548
30848
  "type": "object"
30549
30849
  },
30850
+ "SavedFilter": {
30851
+ "properties": {
30852
+ "createdAt": {
30853
+ "format": "date-time",
30854
+ "type": "string"
30855
+ },
30856
+ "fidelity": {
30857
+ "description": "How much of the UI state `query` captures: `exact`; `partial` (see `unmapped`); `ui_only` — no runnable query. Defaults from whether `query` is present.",
30858
+ "enum": [
30859
+ "exact",
30860
+ "partial",
30861
+ "ui_only"
30862
+ ],
30863
+ "type": "string"
30864
+ },
30865
+ "icon": {
30866
+ "$ref": "#/components/schemas/SavedFilterIcon"
30867
+ },
30868
+ "id": {
30869
+ "type": "string"
30870
+ },
30871
+ "kind": {
30872
+ "description": "`filter` (default) — a saved search; `view` — a saved CRM table layout (columns / order) kept in `uiState`. Views never carry a `query` and have their own 25-per-target quota. Fixed at create.",
30873
+ "enum": [
30874
+ "filter",
30875
+ "view"
30876
+ ],
30877
+ "type": "string"
30878
+ },
30879
+ "name": {
30880
+ "type": "string"
30881
+ },
30882
+ "operationId": {
30883
+ "description": "The operation to run `query` against (e.g. `listOriginators`); null for UI-only targets.",
30884
+ "nullable": true,
30885
+ "type": "string"
30886
+ },
30887
+ "query": {
30888
+ "additionalProperties": {
30889
+ "nullable": true
30890
+ },
30891
+ "nullable": true,
30892
+ "type": "object"
30893
+ },
30894
+ "queryVersion": {
30895
+ "type": "integer"
30896
+ },
30897
+ "stale": {
30898
+ "description": "True when the stored `query` no longer validates against the operation (its contract changed). Re-save or adjust before running.",
30899
+ "type": "boolean"
30900
+ },
30901
+ "staleIssues": {
30902
+ "items": {
30903
+ "properties": {
30904
+ "message": {
30905
+ "type": "string"
30906
+ },
30907
+ "path": {
30908
+ "type": "string"
30909
+ }
30910
+ },
30911
+ "required": [
30912
+ "path",
30913
+ "message"
30914
+ ],
30915
+ "type": "object"
30916
+ },
30917
+ "type": "array"
30918
+ },
30919
+ "target": {
30920
+ "description": "Which list the filter runs against. Market targets run against that entity's list operation (see `operationId`, e.g. listOriginators); `crm_people` / `crm_companies` are UI-only (filtered client-side).",
30921
+ "enum": [
30922
+ "originators",
30923
+ "agents",
30924
+ "companies",
30925
+ "branches",
30926
+ "offices",
30927
+ "loans",
30928
+ "properties",
30929
+ "sales",
30930
+ "crm_people",
30931
+ "crm_companies"
30932
+ ],
30933
+ "type": "string"
30934
+ },
30935
+ "uiState": {
30936
+ "additionalProperties": {
30937
+ "nullable": true
30938
+ },
30939
+ "nullable": true,
30940
+ "type": "object"
30941
+ },
30942
+ "uiStateVersion": {
30943
+ "type": "integer"
30944
+ },
30945
+ "unmapped": {
30946
+ "items": {
30947
+ "type": "string"
30948
+ },
30949
+ "type": "array"
30950
+ },
30951
+ "updatedAt": {
30952
+ "format": "date-time",
30953
+ "type": "string"
30954
+ },
30955
+ "visibility": {
30956
+ "description": "Always `private` in v1.",
30957
+ "enum": [
30958
+ "private",
30959
+ "organization",
30960
+ "shared"
30961
+ ],
30962
+ "type": "string"
30963
+ }
30964
+ },
30965
+ "required": [
30966
+ "id",
30967
+ "name",
30968
+ "target",
30969
+ "kind",
30970
+ "icon",
30971
+ "query",
30972
+ "queryVersion",
30973
+ "uiState",
30974
+ "uiStateVersion",
30975
+ "fidelity",
30976
+ "unmapped",
30977
+ "visibility",
30978
+ "operationId",
30979
+ "stale",
30980
+ "staleIssues",
30981
+ "createdAt",
30982
+ "updatedAt"
30983
+ ],
30984
+ "type": "object"
30985
+ },
30986
+ "SavedFilterError": {
30987
+ "properties": {
30988
+ "code": {
30989
+ "description": "`QUOTA_EXCEEDED` (409, 25 per target + kind).",
30990
+ "type": "string"
30991
+ },
30992
+ "error": {
30993
+ "type": "string"
30994
+ },
30995
+ "issues": {
30996
+ "items": {
30997
+ "properties": {
30998
+ "message": {
30999
+ "type": "string"
31000
+ },
31001
+ "path": {
31002
+ "type": "string"
31003
+ }
31004
+ },
31005
+ "required": [
31006
+ "path",
31007
+ "message"
31008
+ ],
31009
+ "type": "object"
31010
+ },
31011
+ "type": "array"
31012
+ }
31013
+ },
31014
+ "required": [
31015
+ "error"
31016
+ ],
31017
+ "type": "object"
31018
+ },
31019
+ "SavedFilterIcon": {
31020
+ "description": "Optional marker: a color dot, a tinted icon, or an emoji.",
31021
+ "oneOf": [
31022
+ {
31023
+ "properties": {
31024
+ "color": {
31025
+ "pattern": "^#[0-9a-fA-F]{6}$",
31026
+ "type": "string"
31027
+ },
31028
+ "type": {
31029
+ "enum": [
31030
+ "color"
31031
+ ],
31032
+ "type": "string"
31033
+ }
31034
+ },
31035
+ "required": [
31036
+ "type",
31037
+ "color"
31038
+ ],
31039
+ "type": "object"
31040
+ },
31041
+ {
31042
+ "properties": {
31043
+ "color": {
31044
+ "pattern": "^#[0-9a-fA-F]{6}$",
31045
+ "type": "string"
31046
+ },
31047
+ "icon": {
31048
+ "pattern": "^[A-Za-z][A-Za-z0-9]{0,63}$",
31049
+ "type": "string"
31050
+ },
31051
+ "type": {
31052
+ "enum": [
31053
+ "icon"
31054
+ ],
31055
+ "type": "string"
31056
+ }
31057
+ },
31058
+ "required": [
31059
+ "type",
31060
+ "icon",
31061
+ "color"
31062
+ ],
31063
+ "type": "object"
31064
+ },
31065
+ {
31066
+ "properties": {
31067
+ "emoji": {
31068
+ "maxLength": 16,
31069
+ "minLength": 1,
31070
+ "type": "string"
31071
+ },
31072
+ "type": {
31073
+ "enum": [
31074
+ "emoji"
31075
+ ],
31076
+ "type": "string"
31077
+ }
31078
+ },
31079
+ "required": [
31080
+ "type",
31081
+ "emoji"
31082
+ ],
31083
+ "type": "object"
31084
+ },
31085
+ {
31086
+ "nullable": true
31087
+ }
31088
+ ]
31089
+ },
31090
+ "SavedFilterList": {
31091
+ "properties": {
31092
+ "data": {
31093
+ "items": {
31094
+ "$ref": "#/components/schemas/SavedFilter"
31095
+ },
31096
+ "type": "array"
31097
+ }
31098
+ },
31099
+ "required": [
31100
+ "data"
31101
+ ],
31102
+ "type": "object"
31103
+ },
30550
31104
  "ScoredBreakdownRequest": {
30551
31105
  "properties": {
30552
31106
  "period": {
@@ -31441,6 +31995,70 @@
31441
31995
  },
31442
31996
  "type": "object"
31443
31997
  },
31998
+ "UpdateSavedFilterRequest": {
31999
+ "additionalProperties": false,
32000
+ "properties": {
32001
+ "fidelity": {
32002
+ "description": "How much of the UI state `query` captures: `exact`; `partial` (see `unmapped`); `ui_only` — no runnable query. Defaults from whether `query` is present.",
32003
+ "enum": [
32004
+ "exact",
32005
+ "partial",
32006
+ "ui_only"
32007
+ ],
32008
+ "type": "string"
32009
+ },
32010
+ "icon": {
32011
+ "$ref": "#/components/schemas/SavedFilterIcon"
32012
+ },
32013
+ "name": {
32014
+ "maxLength": 100,
32015
+ "minLength": 1,
32016
+ "type": "string"
32017
+ },
32018
+ "query": {
32019
+ "additionalProperties": {
32020
+ "nullable": true
32021
+ },
32022
+ "description": "The target list operation's request body WITHOUT `pagination` (e.g. `{ flatFilters, filters, sort, period }` for listOriginators). Validated against that operation's schema; unknown filter fields are rejected. Null/omitted for a UI-only filter.",
32023
+ "nullable": true,
32024
+ "type": "object"
32025
+ },
32026
+ "uiState": {
32027
+ "additionalProperties": {
32028
+ "nullable": true
32029
+ },
32030
+ "description": "Opaque client UI state used to restore a filter panel (≤16KB). Not interpreted by the API.",
32031
+ "nullable": true,
32032
+ "type": "object"
32033
+ },
32034
+ "uiStateVersion": {
32035
+ "minimum": 1,
32036
+ "type": "integer"
32037
+ },
32038
+ "unmapped": {
32039
+ "description": "UI filter keys the client could not express in `query` (set with `fidelity: partial`).",
32040
+ "items": {
32041
+ "maxLength": 200,
32042
+ "minLength": 1,
32043
+ "type": "string"
32044
+ },
32045
+ "maxItems": 100,
32046
+ "type": "array"
32047
+ }
32048
+ },
32049
+ "type": "object"
32050
+ },
32051
+ "UpdateSavedFilterResult": {
32052
+ "properties": {
32053
+ "data": {
32054
+ "$ref": "#/components/schemas/SavedFilter"
32055
+ }
32056
+ },
32057
+ "required": [
32058
+ "data"
32059
+ ],
32060
+ "type": "object"
32061
+ },
31444
32062
  "WatchedAgent": {
31445
32063
  "properties": {
31446
32064
  "addedAt": {
@@ -31692,7 +32310,7 @@
31692
32310
  "info": {
31693
32311
  "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.",
31694
32312
  "title": "ModelMatch API",
31695
- "version": "3.58.0"
32313
+ "version": "3.62.0"
31696
32314
  },
31697
32315
  "openapi": "3.1.0",
31698
32316
  "paths": {
@@ -35393,7 +36011,7 @@
35393
36011
  "schema": {
35394
36012
  "description": "Individual NMLS id.",
35395
36013
  "example": "3352",
35396
- "pattern": "^\\d+$/u",
36014
+ "pattern": "^\\d+$",
35397
36015
  "type": "string"
35398
36016
  }
35399
36017
  }
@@ -35531,7 +36149,7 @@
35531
36149
  "schema": {
35532
36150
  "description": "ECS task id returned by the launch route.",
35533
36151
  "example": "0123456789abcdef0123456789abcdef",
35534
- "pattern": "^[a-f0-9]{32}$/u",
36152
+ "pattern": "^[a-f0-9]{32}$",
35535
36153
  "type": "string"
35536
36154
  }
35537
36155
  }
@@ -42205,7 +42823,7 @@
42205
42823
  "required": false,
42206
42824
  "schema": {
42207
42825
  "description": "Window start, inclusive (YYYY-MM-DD). Overrides `period`.",
42208
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
42826
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42209
42827
  "type": "string"
42210
42828
  }
42211
42829
  },
@@ -42216,7 +42834,7 @@
42216
42834
  "required": false,
42217
42835
  "schema": {
42218
42836
  "description": "Window end, inclusive (YYYY-MM-DD). Overrides `period`.",
42219
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
42837
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42220
42838
  "type": "string"
42221
42839
  }
42222
42840
  },
@@ -42227,7 +42845,7 @@
42227
42845
  "required": false,
42228
42846
  "schema": {
42229
42847
  "description": "Drill into one hire cohort (`YYYY-MM`): `data` then lists its early departures and recent starts.",
42230
- "pattern": "^\\d{4}-\\d{2}$/u",
42848
+ "pattern": "^\\d{4}-\\d{2}$",
42231
42849
  "type": "string"
42232
42850
  }
42233
42851
  },
@@ -42349,7 +42967,7 @@
42349
42967
  "required": false,
42350
42968
  "schema": {
42351
42969
  "description": "Window start, inclusive (YYYY-MM-DD). Overrides `period`.",
42352
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
42970
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42353
42971
  "type": "string"
42354
42972
  }
42355
42973
  },
@@ -42360,7 +42978,7 @@
42360
42978
  "required": false,
42361
42979
  "schema": {
42362
42980
  "description": "Window end, inclusive (YYYY-MM-DD). Overrides `period`.",
42363
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
42981
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42364
42982
  "type": "string"
42365
42983
  }
42366
42984
  }
@@ -42487,7 +43105,7 @@
42487
43105
  "required": false,
42488
43106
  "schema": {
42489
43107
  "description": "Window start, inclusive (YYYY-MM-DD). Overrides `period`.",
42490
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43108
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42491
43109
  "type": "string"
42492
43110
  }
42493
43111
  },
@@ -42498,7 +43116,7 @@
42498
43116
  "required": false,
42499
43117
  "schema": {
42500
43118
  "description": "Window end, inclusive (YYYY-MM-DD). Overrides `period`.",
42501
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43119
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42502
43120
  "type": "string"
42503
43121
  }
42504
43122
  },
@@ -42509,7 +43127,7 @@
42509
43127
  "required": false,
42510
43128
  "schema": {
42511
43129
  "description": "Roster AS OF this date (YYYY-MM-DD): loan officers with a record covering it. Replaces the window; `status` must be `active` or omitted.",
42512
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43130
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42513
43131
  "type": "string"
42514
43132
  }
42515
43133
  },
@@ -42685,7 +43303,7 @@
42685
43303
  "required": false,
42686
43304
  "schema": {
42687
43305
  "description": "Window start, inclusive (YYYY-MM-DD). Overrides `period`.",
42688
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43306
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42689
43307
  "type": "string"
42690
43308
  }
42691
43309
  },
@@ -42696,7 +43314,7 @@
42696
43314
  "required": false,
42697
43315
  "schema": {
42698
43316
  "description": "Window end, inclusive (YYYY-MM-DD). Overrides `period`.",
42699
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43317
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
42700
43318
  "type": "string"
42701
43319
  }
42702
43320
  },
@@ -43096,7 +43714,7 @@
43096
43714
  "required": false,
43097
43715
  "schema": {
43098
43716
  "description": "Window start, inclusive (YYYY-MM-DD). Overrides `period`.",
43099
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43717
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
43100
43718
  "type": "string"
43101
43719
  }
43102
43720
  },
@@ -43107,7 +43725,7 @@
43107
43725
  "required": false,
43108
43726
  "schema": {
43109
43727
  "description": "Window end, inclusive (YYYY-MM-DD). Overrides `period`.",
43110
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
43728
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
43111
43729
  "type": "string"
43112
43730
  }
43113
43731
  },
@@ -55909,6 +56527,7 @@
55909
56527
  },
55910
56528
  "/v1/loans/analytics/chart": {
55911
56529
  "post": {
56530
+ "description": "Buckets the filtered loans by `slice` and ranks them by `measure`, descending. Party slices (`originator`, `broker`, `loanCompany`) are pageable: send `size` (1–1000) and follow `nextCursor` via `after` to walk the whole ranking, with `total` giving the (approximate) number of distinct parties. Other slices return their fixed top-N. `period` accepts rolling windows (`last30Days`, `last60Days`, `last90Days`, `previousMonth`, `currentMonth`) resolved in UTC; `/loans/analytics/summary` with the same filters and period returns the matching population totals.",
55912
56531
  "operationId": "loansAnalyticsChart",
55913
56532
  "requestBody": {
55914
56533
  "content": {
@@ -57257,7 +57876,7 @@
57257
57876
  "schema": {
57258
57877
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
57259
57878
  "example": "last_12_months",
57260
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
57879
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
57261
57880
  "type": "string"
57262
57881
  }
57263
57882
  },
@@ -57269,7 +57888,7 @@
57269
57888
  "schema": {
57270
57889
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
57271
57890
  "example": "TX",
57272
- "pattern": "^[A-Za-z]{2}$/u",
57891
+ "pattern": "^[A-Za-z]{2}$",
57273
57892
  "type": "string"
57274
57893
  }
57275
57894
  },
@@ -57281,7 +57900,7 @@
57281
57900
  "schema": {
57282
57901
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
57283
57902
  "example": "WA,OR,ID",
57284
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
57903
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
57285
57904
  "type": "string"
57286
57905
  }
57287
57906
  },
@@ -57400,7 +58019,7 @@
57400
58019
  "schema": {
57401
58020
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
57402
58021
  "example": "last_12_months",
57403
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
58022
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
57404
58023
  "type": "string"
57405
58024
  }
57406
58025
  },
@@ -57412,7 +58031,7 @@
57412
58031
  "schema": {
57413
58032
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
57414
58033
  "example": "TX",
57415
- "pattern": "^[A-Za-z]{2}$/u",
58034
+ "pattern": "^[A-Za-z]{2}$",
57416
58035
  "type": "string"
57417
58036
  }
57418
58037
  },
@@ -57424,7 +58043,7 @@
57424
58043
  "schema": {
57425
58044
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
57426
58045
  "example": "WA,OR,ID",
57427
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
58046
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
57428
58047
  "type": "string"
57429
58048
  }
57430
58049
  },
@@ -57448,7 +58067,7 @@
57448
58067
  "schema": {
57449
58068
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
57450
58069
  "example": "BANK,CU",
57451
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
58070
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
57452
58071
  "type": "string"
57453
58072
  }
57454
58073
  },
@@ -57567,7 +58186,7 @@
57567
58186
  "schema": {
57568
58187
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
57569
58188
  "example": "last_12_months",
57570
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
58189
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
57571
58190
  "type": "string"
57572
58191
  }
57573
58192
  },
@@ -57579,7 +58198,7 @@
57579
58198
  "schema": {
57580
58199
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
57581
58200
  "example": "TX",
57582
- "pattern": "^[A-Za-z]{2}$/u",
58201
+ "pattern": "^[A-Za-z]{2}$",
57583
58202
  "type": "string"
57584
58203
  }
57585
58204
  },
@@ -57591,7 +58210,7 @@
57591
58210
  "schema": {
57592
58211
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
57593
58212
  "example": "WA,OR,ID",
57594
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
58213
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
57595
58214
  "type": "string"
57596
58215
  }
57597
58216
  },
@@ -57615,7 +58234,7 @@
57615
58234
  "schema": {
57616
58235
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
57617
58236
  "example": "BANK,CU",
57618
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
58237
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
57619
58238
  "type": "string"
57620
58239
  }
57621
58240
  },
@@ -57737,7 +58356,7 @@
57737
58356
  "schema": {
57738
58357
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
57739
58358
  "example": "last_12_months",
57740
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
58359
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
57741
58360
  "type": "string"
57742
58361
  }
57743
58362
  },
@@ -57749,7 +58368,7 @@
57749
58368
  "schema": {
57750
58369
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
57751
58370
  "example": "TX",
57752
- "pattern": "^[A-Za-z]{2}$/u",
58371
+ "pattern": "^[A-Za-z]{2}$",
57753
58372
  "type": "string"
57754
58373
  }
57755
58374
  },
@@ -57761,7 +58380,7 @@
57761
58380
  "schema": {
57762
58381
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
57763
58382
  "example": "WA,OR,ID",
57764
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
58383
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
57765
58384
  "type": "string"
57766
58385
  }
57767
58386
  },
@@ -57785,7 +58404,7 @@
57785
58404
  "schema": {
57786
58405
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
57787
58406
  "example": "BANK,CU",
57788
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
58407
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
57789
58408
  "type": "string"
57790
58409
  }
57791
58410
  },
@@ -57918,7 +58537,7 @@
57918
58537
  "schema": {
57919
58538
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
57920
58539
  "example": "last_12_months",
57921
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
58540
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
57922
58541
  "type": "string"
57923
58542
  }
57924
58543
  },
@@ -57930,7 +58549,7 @@
57930
58549
  "schema": {
57931
58550
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
57932
58551
  "example": "TX",
57933
- "pattern": "^[A-Za-z]{2}$/u",
58552
+ "pattern": "^[A-Za-z]{2}$",
57934
58553
  "type": "string"
57935
58554
  }
57936
58555
  },
@@ -57942,7 +58561,7 @@
57942
58561
  "schema": {
57943
58562
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
57944
58563
  "example": "WA,OR,ID",
57945
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
58564
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
57946
58565
  "type": "string"
57947
58566
  }
57948
58567
  },
@@ -57966,7 +58585,7 @@
57966
58585
  "schema": {
57967
58586
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
57968
58587
  "example": "BANK,CU",
57969
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
58588
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
57970
58589
  "type": "string"
57971
58590
  }
57972
58591
  },
@@ -58099,7 +58718,7 @@
58099
58718
  "schema": {
58100
58719
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
58101
58720
  "example": "last_12_months",
58102
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
58721
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
58103
58722
  "type": "string"
58104
58723
  }
58105
58724
  },
@@ -58111,7 +58730,7 @@
58111
58730
  "schema": {
58112
58731
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
58113
58732
  "example": "TX",
58114
- "pattern": "^[A-Za-z]{2}$/u",
58733
+ "pattern": "^[A-Za-z]{2}$",
58115
58734
  "type": "string"
58116
58735
  }
58117
58736
  },
@@ -58123,7 +58742,7 @@
58123
58742
  "schema": {
58124
58743
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
58125
58744
  "example": "WA,OR,ID",
58126
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
58745
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
58127
58746
  "type": "string"
58128
58747
  }
58129
58748
  },
@@ -58147,7 +58766,7 @@
58147
58766
  "schema": {
58148
58767
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
58149
58768
  "example": "BANK,CU",
58150
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
58769
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
58151
58770
  "type": "string"
58152
58771
  }
58153
58772
  },
@@ -58327,7 +58946,7 @@
58327
58946
  "required": false,
58328
58947
  "schema": {
58329
58948
  "description": "Calendar year to rank (default: last year).",
58330
- "pattern": "^\\d{4}$/u",
58949
+ "pattern": "^\\d{4}$",
58331
58950
  "type": "string"
58332
58951
  }
58333
58952
  },
@@ -58456,7 +59075,7 @@
58456
59075
  "schema": {
58457
59076
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
58458
59077
  "example": "last_12_months",
58459
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
59078
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
58460
59079
  "type": "string"
58461
59080
  }
58462
59081
  },
@@ -58468,7 +59087,7 @@
58468
59087
  "schema": {
58469
59088
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
58470
59089
  "example": "TX",
58471
- "pattern": "^[A-Za-z]{2}$/u",
59090
+ "pattern": "^[A-Za-z]{2}$",
58472
59091
  "type": "string"
58473
59092
  }
58474
59093
  },
@@ -58480,7 +59099,7 @@
58480
59099
  "schema": {
58481
59100
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
58482
59101
  "example": "WA,OR,ID",
58483
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
59102
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
58484
59103
  "type": "string"
58485
59104
  }
58486
59105
  },
@@ -58504,7 +59123,7 @@
58504
59123
  "schema": {
58505
59124
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
58506
59125
  "example": "BANK,CU",
58507
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
59126
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
58508
59127
  "type": "string"
58509
59128
  }
58510
59129
  }
@@ -58611,7 +59230,7 @@
58611
59230
  "schema": {
58612
59231
  "description": "Reporting window. `last_N_months` = the N most recent COMPLETED calendar months (the current month is excluded); `year_to_date` = Jan 1 through the end of last month; `YYYY` (or `fy_YYYY`) = that calendar year; `all_time`. Default `last_12_months`.",
58613
59232
  "example": "last_12_months",
58614
- "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$/u",
59233
+ "pattern": "^(?:last_\\d{1,3}_months|year_to_date|all_time|(?:fy_|fiscal_)?\\d{4})$",
58615
59234
  "type": "string"
58616
59235
  }
58617
59236
  },
@@ -58623,7 +59242,7 @@
58623
59242
  "schema": {
58624
59243
  "description": "Two-letter state. Matches the loan officer's CURRENT NMLS location, not where they were when they moved.",
58625
59244
  "example": "TX",
58626
- "pattern": "^[A-Za-z]{2}$/u",
59245
+ "pattern": "^[A-Za-z]{2}$",
58627
59246
  "type": "string"
58628
59247
  }
58629
59248
  },
@@ -58635,7 +59254,7 @@
58635
59254
  "schema": {
58636
59255
  "description": "Comma-separated two-letter states (e.g. a region). ORed; combined with `state` when both are sent.",
58637
59256
  "example": "WA,OR,ID",
58638
- "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$/u",
59257
+ "pattern": "^[A-Za-z]{2}(?:,[A-Za-z]{2})*$",
58639
59258
  "type": "string"
58640
59259
  }
58641
59260
  },
@@ -58659,7 +59278,7 @@
58659
59278
  "schema": {
58660
59279
  "description": "Comma-separated company categories (`BANK`, `CU`, `OTHER`) applied to the company whose roster changed — the destination for arrivals, the company left for departures.",
58661
59280
  "example": "BANK,CU",
58662
- "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$/u",
59281
+ "pattern": "^(?:BANK|CU|OTHER)(?:,(?:BANK|CU|OTHER))*$",
58663
59282
  "type": "string"
58664
59283
  }
58665
59284
  },
@@ -58958,7 +59577,7 @@
58958
59577
  "required": false,
58959
59578
  "schema": {
58960
59579
  "description": "Usage window start (YYYY-MM-DD, inclusive)",
58961
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
59580
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
58962
59581
  "type": "string"
58963
59582
  }
58964
59583
  },
@@ -58969,7 +59588,7 @@
58969
59588
  "required": false,
58970
59589
  "schema": {
58971
59590
  "description": "Usage window end (YYYY-MM-DD, inclusive)",
58972
- "pattern": "^\\d{4}-\\d{2}-\\d{2}$/u",
59591
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
58973
59592
  "type": "string"
58974
59593
  }
58975
59594
  }
@@ -62411,6 +63030,17 @@
62411
63030
  "description": "Pagination cursor from a previous response",
62412
63031
  "type": "string"
62413
63032
  }
63033
+ },
63034
+ {
63035
+ "description": "Return only this property's enrichment (zero or one item). The id is the one in the credit ledger's `enrich:<propertyId>` reason.",
63036
+ "in": "query",
63037
+ "name": "propertyId",
63038
+ "required": false,
63039
+ "schema": {
63040
+ "description": "Return only this property's enrichment (zero or one item). The id is the one in the credit ledger's `enrich:<propertyId>` reason.",
63041
+ "minLength": 1,
63042
+ "type": "string"
63043
+ }
62414
63044
  }
62415
63045
  ],
62416
63046
  "responses": {
@@ -63037,34 +63667,440 @@
63037
63667
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63038
63668
  }
63039
63669
  },
63040
- "summary": "Search sales with filters",
63670
+ "summary": "Search sales with filters",
63671
+ "tags": [
63672
+ "Sales"
63673
+ ]
63674
+ }
63675
+ },
63676
+ "/v1/sales/analytics/chart": {
63677
+ "post": {
63678
+ "operationId": "salesAnalyticsChart",
63679
+ "requestBody": {
63680
+ "content": {
63681
+ "application/json": {
63682
+ "schema": {
63683
+ "$ref": "#/components/schemas/SaleChartRequest"
63684
+ }
63685
+ }
63686
+ }
63687
+ },
63688
+ "responses": {
63689
+ "200": {
63690
+ "content": {
63691
+ "application/json": {
63692
+ "schema": {
63693
+ "$ref": "#/components/schemas/ChartResponse"
63694
+ }
63695
+ }
63696
+ },
63697
+ "description": "Chart data"
63698
+ },
63699
+ "400": {
63700
+ "content": {
63701
+ "application/json": {
63702
+ "schema": {
63703
+ "$ref": "#/components/schemas/ErrorResponse"
63704
+ }
63705
+ }
63706
+ },
63707
+ "description": "Validation error"
63708
+ },
63709
+ "401": {
63710
+ "content": {
63711
+ "application/json": {
63712
+ "schema": {
63713
+ "$ref": "#/components/schemas/ErrorResponse"
63714
+ }
63715
+ }
63716
+ },
63717
+ "description": "Unauthorized"
63718
+ },
63719
+ "403": {
63720
+ "content": {
63721
+ "application/json": {
63722
+ "schema": {
63723
+ "$ref": "#/components/schemas/ErrorResponse"
63724
+ }
63725
+ }
63726
+ },
63727
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
63728
+ },
63729
+ "404": {
63730
+ "content": {
63731
+ "application/json": {
63732
+ "schema": {
63733
+ "$ref": "#/components/schemas/ErrorResponse"
63734
+ }
63735
+ }
63736
+ },
63737
+ "description": "Not found"
63738
+ },
63739
+ "413": {
63740
+ "content": {
63741
+ "application/json": {
63742
+ "schema": {
63743
+ "$ref": "#/components/schemas/PayloadTooLarge"
63744
+ }
63745
+ }
63746
+ },
63747
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63748
+ }
63749
+ },
63750
+ "summary": "Configurable chart (measure × slice) over the market",
63751
+ "tags": [
63752
+ "Sales"
63753
+ ]
63754
+ }
63755
+ },
63756
+ "/v1/sales/analytics/summary": {
63757
+ "post": {
63758
+ "operationId": "salesAnalyticsSummary",
63759
+ "requestBody": {
63760
+ "content": {
63761
+ "application/json": {
63762
+ "schema": {
63763
+ "$ref": "#/components/schemas/SaleSummaryRequest"
63764
+ }
63765
+ }
63766
+ }
63767
+ },
63768
+ "responses": {
63769
+ "200": {
63770
+ "content": {
63771
+ "application/json": {
63772
+ "schema": {
63773
+ "$ref": "#/components/schemas/SidedSummaryResponse"
63774
+ }
63775
+ }
63776
+ },
63777
+ "description": "Sided market summary with previous-period comparison"
63778
+ },
63779
+ "400": {
63780
+ "content": {
63781
+ "application/json": {
63782
+ "schema": {
63783
+ "$ref": "#/components/schemas/ErrorResponse"
63784
+ }
63785
+ }
63786
+ },
63787
+ "description": "Validation error"
63788
+ },
63789
+ "401": {
63790
+ "content": {
63791
+ "application/json": {
63792
+ "schema": {
63793
+ "$ref": "#/components/schemas/ErrorResponse"
63794
+ }
63795
+ }
63796
+ },
63797
+ "description": "Unauthorized"
63798
+ },
63799
+ "403": {
63800
+ "content": {
63801
+ "application/json": {
63802
+ "schema": {
63803
+ "$ref": "#/components/schemas/ErrorResponse"
63804
+ }
63805
+ }
63806
+ },
63807
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
63808
+ },
63809
+ "404": {
63810
+ "content": {
63811
+ "application/json": {
63812
+ "schema": {
63813
+ "$ref": "#/components/schemas/ErrorResponse"
63814
+ }
63815
+ }
63816
+ },
63817
+ "description": "Not found"
63818
+ },
63819
+ "413": {
63820
+ "content": {
63821
+ "application/json": {
63822
+ "schema": {
63823
+ "$ref": "#/components/schemas/PayloadTooLarge"
63824
+ }
63825
+ }
63826
+ },
63827
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63828
+ }
63829
+ },
63830
+ "summary": "Market summary with buyer/seller/dual split + previous period",
63831
+ "tags": [
63832
+ "Sales"
63833
+ ]
63834
+ }
63835
+ },
63836
+ "/v1/sales/analytics/time-series": {
63837
+ "post": {
63838
+ "operationId": "salesAnalyticsTimeSeries",
63839
+ "requestBody": {
63840
+ "content": {
63841
+ "application/json": {
63842
+ "schema": {
63843
+ "$ref": "#/components/schemas/SaleTimeSeriesRequest"
63844
+ }
63845
+ }
63846
+ }
63847
+ },
63848
+ "responses": {
63849
+ "200": {
63850
+ "content": {
63851
+ "application/json": {
63852
+ "schema": {
63853
+ "$ref": "#/components/schemas/SalesTimeSeriesResponse"
63854
+ }
63855
+ }
63856
+ },
63857
+ "description": "Time-series with side splits + previous period. Every side carries `volume`, `units`, and `measureValue` for the requested `measure`."
63858
+ },
63859
+ "400": {
63860
+ "content": {
63861
+ "application/json": {
63862
+ "schema": {
63863
+ "$ref": "#/components/schemas/ErrorResponse"
63864
+ }
63865
+ }
63866
+ },
63867
+ "description": "Validation error"
63868
+ },
63869
+ "401": {
63870
+ "content": {
63871
+ "application/json": {
63872
+ "schema": {
63873
+ "$ref": "#/components/schemas/ErrorResponse"
63874
+ }
63875
+ }
63876
+ },
63877
+ "description": "Unauthorized"
63878
+ },
63879
+ "403": {
63880
+ "content": {
63881
+ "application/json": {
63882
+ "schema": {
63883
+ "$ref": "#/components/schemas/ErrorResponse"
63884
+ }
63885
+ }
63886
+ },
63887
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
63888
+ },
63889
+ "404": {
63890
+ "content": {
63891
+ "application/json": {
63892
+ "schema": {
63893
+ "$ref": "#/components/schemas/ErrorResponse"
63894
+ }
63895
+ }
63896
+ },
63897
+ "description": "Not found"
63898
+ },
63899
+ "413": {
63900
+ "content": {
63901
+ "application/json": {
63902
+ "schema": {
63903
+ "$ref": "#/components/schemas/PayloadTooLarge"
63904
+ }
63905
+ }
63906
+ },
63907
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63908
+ }
63909
+ },
63910
+ "summary": "Volume/units over time with buyer/seller/dual split",
63911
+ "tags": [
63912
+ "Sales"
63913
+ ]
63914
+ }
63915
+ },
63916
+ "/v1/sales/count": {
63917
+ "post": {
63918
+ "description": "Return the exact number of sales matching the given filters. Accepts the same request body as `listSales` (pagination and sort are ignored). Use this when you only need a total; to get the matching sales themselves, send the same request body to `listSales`.",
63919
+ "operationId": "countSales",
63920
+ "requestBody": {
63921
+ "content": {
63922
+ "application/json": {
63923
+ "schema": {
63924
+ "$ref": "#/components/schemas/SaleListRequest"
63925
+ }
63926
+ }
63927
+ }
63928
+ },
63929
+ "responses": {
63930
+ "200": {
63931
+ "content": {
63932
+ "application/json": {
63933
+ "schema": {
63934
+ "$ref": "#/components/schemas/CountResponse"
63935
+ }
63936
+ }
63937
+ },
63938
+ "description": "Exact count of matching sales"
63939
+ },
63940
+ "400": {
63941
+ "content": {
63942
+ "application/json": {
63943
+ "schema": {
63944
+ "$ref": "#/components/schemas/ErrorResponse"
63945
+ }
63946
+ }
63947
+ },
63948
+ "description": "Validation error"
63949
+ },
63950
+ "401": {
63951
+ "content": {
63952
+ "application/json": {
63953
+ "schema": {
63954
+ "$ref": "#/components/schemas/ErrorResponse"
63955
+ }
63956
+ }
63957
+ },
63958
+ "description": "Unauthorized"
63959
+ },
63960
+ "403": {
63961
+ "content": {
63962
+ "application/json": {
63963
+ "schema": {
63964
+ "$ref": "#/components/schemas/ErrorResponse"
63965
+ }
63966
+ }
63967
+ },
63968
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
63969
+ },
63970
+ "404": {
63971
+ "content": {
63972
+ "application/json": {
63973
+ "schema": {
63974
+ "$ref": "#/components/schemas/ErrorResponse"
63975
+ }
63976
+ }
63977
+ },
63978
+ "description": "Not found"
63979
+ },
63980
+ "413": {
63981
+ "content": {
63982
+ "application/json": {
63983
+ "schema": {
63984
+ "$ref": "#/components/schemas/PayloadTooLarge"
63985
+ }
63986
+ }
63987
+ },
63988
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63989
+ }
63990
+ },
63991
+ "summary": "Count sales matching filters",
63041
63992
  "tags": [
63042
63993
  "Sales"
63043
- ]
63994
+ ],
63995
+ "x-mm-records-via": "listSales"
63044
63996
  }
63045
63997
  },
63046
- "/v1/sales/analytics/chart": {
63047
- "post": {
63048
- "operationId": "salesAnalyticsChart",
63049
- "requestBody": {
63050
- "content": {
63051
- "application/json": {
63052
- "schema": {
63053
- "$ref": "#/components/schemas/SaleChartRequest"
63054
- }
63998
+ "/v1/sales/{id}": {
63999
+ "get": {
64000
+ "operationId": "getSale",
64001
+ "parameters": [
64002
+ {
64003
+ "description": "Sale document ID",
64004
+ "in": "path",
64005
+ "name": "id",
64006
+ "required": true,
64007
+ "schema": {
64008
+ "description": "Sale document ID",
64009
+ "type": "string"
63055
64010
  }
63056
64011
  }
64012
+ ],
64013
+ "responses": {
64014
+ "200": {
64015
+ "content": {
64016
+ "application/json": {
64017
+ "schema": {
64018
+ "$ref": "#/components/schemas/SaleDetailResponse"
64019
+ }
64020
+ }
64021
+ },
64022
+ "description": "Sale detail"
64023
+ },
64024
+ "404": {
64025
+ "content": {
64026
+ "application/json": {
64027
+ "schema": {
64028
+ "$ref": "#/components/schemas/ErrorResponse"
64029
+ }
64030
+ }
64031
+ },
64032
+ "description": "Sale not found"
64033
+ },
64034
+ "413": {
64035
+ "content": {
64036
+ "application/json": {
64037
+ "schema": {
64038
+ "$ref": "#/components/schemas/PayloadTooLarge"
64039
+ }
64040
+ }
64041
+ },
64042
+ "description": "Response payload too large"
64043
+ }
63057
64044
  },
64045
+ "summary": "Get sale by ID",
64046
+ "tags": [
64047
+ "Sales"
64048
+ ]
64049
+ }
64050
+ },
64051
+ "/v1/sales/{id}/related": {
64052
+ "get": {
64053
+ "description": "Return the ids of records of another type that are linked to this record. Links resolve by identifier, strongest identifier first, and equally-trusted identifiers are combined — a company's linked loans cover every role it holds on a loan, not just the first one found. The response reports which strength was used: `primary` is the canonical identifier for that pair, `secondary` is a real but weaker alternate used only when the record carries nothing better. This is a lightweight index: it returns ids and a count, not full records. For filtering, sorting, pagination and full detail, use the named endpoint for the pair (for example the originator loans endpoint) and fetch records by id.",
64054
+ "operationId": "saleRelated",
64055
+ "parameters": [
64056
+ {
64057
+ "description": "Sale id",
64058
+ "in": "path",
64059
+ "name": "id",
64060
+ "required": true,
64061
+ "schema": {
64062
+ "description": "Sale id",
64063
+ "type": "string"
64064
+ }
64065
+ },
64066
+ {
64067
+ "description": "Type of record to link to",
64068
+ "in": "query",
64069
+ "name": "to",
64070
+ "required": true,
64071
+ "schema": {
64072
+ "description": "Type of record to link to",
64073
+ "enum": [
64074
+ "agents",
64075
+ "loans",
64076
+ "properties"
64077
+ ],
64078
+ "type": "string"
64079
+ }
64080
+ },
64081
+ {
64082
+ "description": "Maximum ids to return (default 25, max 200)",
64083
+ "in": "query",
64084
+ "name": "limit",
64085
+ "required": false,
64086
+ "schema": {
64087
+ "description": "Maximum ids to return (default 25, max 200)",
64088
+ "maximum": 200,
64089
+ "minimum": 1,
64090
+ "type": "integer"
64091
+ }
64092
+ }
64093
+ ],
63058
64094
  "responses": {
63059
64095
  "200": {
63060
64096
  "content": {
63061
64097
  "application/json": {
63062
64098
  "schema": {
63063
- "$ref": "#/components/schemas/ChartResponse"
64099
+ "$ref": "#/components/schemas/RelatedResponse"
63064
64100
  }
63065
64101
  }
63066
64102
  },
63067
- "description": "Chart data"
64103
+ "description": "Linked record ids"
63068
64104
  },
63069
64105
  "400": {
63070
64106
  "content": {
@@ -63104,7 +64140,7 @@
63104
64140
  }
63105
64141
  }
63106
64142
  },
63107
- "description": "Not found"
64143
+ "description": "Record not found"
63108
64144
  },
63109
64145
  "413": {
63110
64146
  "content": {
@@ -63117,34 +64153,64 @@
63117
64153
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63118
64154
  }
63119
64155
  },
63120
- "summary": "Configurable chart (measure × slice) over the market",
64156
+ "summary": "Records linked to this one",
63121
64157
  "tags": [
63122
- "Sales"
64158
+ "Related"
63123
64159
  ]
63124
64160
  }
63125
64161
  },
63126
- "/v1/sales/analytics/summary": {
63127
- "post": {
63128
- "operationId": "salesAnalyticsSummary",
63129
- "requestBody": {
63130
- "content": {
63131
- "application/json": {
63132
- "schema": {
63133
- "$ref": "#/components/schemas/SaleSummaryRequest"
63134
- }
64162
+ "/v1/saved-filters": {
64163
+ "get": {
64164
+ "description": "The caller's saved filters (and saved CRM views) in the active organization, oldest first, optionally narrowed by target and kind.",
64165
+ "operationId": "listSavedFilters",
64166
+ "parameters": [
64167
+ {
64168
+ "description": "Which list the filter runs against. Market targets run against that entity's list operation (see `operationId`, e.g. listOriginators); `crm_people` / `crm_companies` are UI-only (filtered client-side).",
64169
+ "in": "query",
64170
+ "name": "target",
64171
+ "required": false,
64172
+ "schema": {
64173
+ "description": "Which list the filter runs against. Market targets run against that entity's list operation (see `operationId`, e.g. listOriginators); `crm_people` / `crm_companies` are UI-only (filtered client-side).",
64174
+ "enum": [
64175
+ "originators",
64176
+ "agents",
64177
+ "companies",
64178
+ "branches",
64179
+ "offices",
64180
+ "loans",
64181
+ "properties",
64182
+ "sales",
64183
+ "crm_people",
64184
+ "crm_companies"
64185
+ ],
64186
+ "type": "string"
64187
+ }
64188
+ },
64189
+ {
64190
+ "description": "`filter` (default) — a saved search; `view` — a saved CRM table layout (columns / order) kept in `uiState`. Views never carry a `query` and have their own 25-per-target quota. Fixed at create.",
64191
+ "in": "query",
64192
+ "name": "kind",
64193
+ "required": false,
64194
+ "schema": {
64195
+ "description": "`filter` (default) — a saved search; `view` — a saved CRM table layout (columns / order) kept in `uiState`. Views never carry a `query` and have their own 25-per-target quota. Fixed at create.",
64196
+ "enum": [
64197
+ "filter",
64198
+ "view"
64199
+ ],
64200
+ "type": "string"
63135
64201
  }
63136
64202
  }
63137
- },
64203
+ ],
63138
64204
  "responses": {
63139
64205
  "200": {
63140
64206
  "content": {
63141
64207
  "application/json": {
63142
64208
  "schema": {
63143
- "$ref": "#/components/schemas/SidedSummaryResponse"
64209
+ "$ref": "#/components/schemas/SavedFilterList"
63144
64210
  }
63145
64211
  }
63146
64212
  },
63147
- "description": "Sided market summary with previous-period comparison"
64213
+ "description": "Saved filters."
63148
64214
  },
63149
64215
  "400": {
63150
64216
  "content": {
@@ -63197,44 +64263,43 @@
63197
64263
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63198
64264
  }
63199
64265
  },
63200
- "summary": "Market summary with buyer/seller/dual split + previous period",
64266
+ "summary": "List your saved filters",
63201
64267
  "tags": [
63202
- "Sales"
64268
+ "Saved Filters"
63203
64269
  ]
63204
- }
63205
- },
63206
- "/v1/sales/analytics/time-series": {
64270
+ },
63207
64271
  "post": {
63208
- "operationId": "salesAnalyticsTimeSeries",
64272
+ "description": "Save a named filter for a list target. For a market target, `query` is the target operation's request body without `pagination` (e.g. the body you would send to listOriginators) and is validated against it. Limit: 25 per target.",
64273
+ "operationId": "createSavedFilter",
63209
64274
  "requestBody": {
63210
64275
  "content": {
63211
64276
  "application/json": {
63212
64277
  "schema": {
63213
- "$ref": "#/components/schemas/SaleTimeSeriesRequest"
64278
+ "$ref": "#/components/schemas/CreateSavedFilterRequest"
63214
64279
  }
63215
64280
  }
63216
64281
  }
63217
64282
  },
63218
64283
  "responses": {
63219
- "200": {
64284
+ "201": {
63220
64285
  "content": {
63221
64286
  "application/json": {
63222
64287
  "schema": {
63223
- "$ref": "#/components/schemas/SalesTimeSeriesResponse"
64288
+ "$ref": "#/components/schemas/CreateSavedFilterResult"
63224
64289
  }
63225
64290
  }
63226
64291
  },
63227
- "description": "Time-series with side splits + previous period. Every side carries `volume`, `units`, and `measureValue` for the requested `measure`."
64292
+ "description": "The saved filter."
63228
64293
  },
63229
64294
  "400": {
63230
64295
  "content": {
63231
64296
  "application/json": {
63232
64297
  "schema": {
63233
- "$ref": "#/components/schemas/ErrorResponse"
64298
+ "$ref": "#/components/schemas/SavedFilterError"
63234
64299
  }
63235
64300
  }
63236
64301
  },
63237
- "description": "Validation error"
64302
+ "description": "Validation error — including a `query` that is not a valid request for the target's operation (`issues` lists each problem)."
63238
64303
  },
63239
64304
  "401": {
63240
64305
  "content": {
@@ -63266,6 +64331,16 @@
63266
64331
  },
63267
64332
  "description": "Not found"
63268
64333
  },
64334
+ "409": {
64335
+ "content": {
64336
+ "application/json": {
64337
+ "schema": {
64338
+ "$ref": "#/components/schemas/SavedFilterError"
64339
+ }
64340
+ }
64341
+ },
64342
+ "description": "`QUOTA_EXCEEDED` — 25 saved filters for this target already; update or delete one."
64343
+ },
63269
64344
  "413": {
63270
64345
  "content": {
63271
64346
  "application/json": {
@@ -63277,35 +64352,41 @@
63277
64352
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63278
64353
  }
63279
64354
  },
63280
- "summary": "Volume/units over time with buyer/seller/dual split",
64355
+ "summary": "Save a filter",
63281
64356
  "tags": [
63282
- "Sales"
64357
+ "Saved Filters"
63283
64358
  ]
63284
64359
  }
63285
64360
  },
63286
- "/v1/sales/count": {
63287
- "post": {
63288
- "description": "Return the exact number of sales matching the given filters. Accepts the same request body as `listSales` (pagination and sort are ignored). Use this when you only need a total; to get the matching sales themselves, send the same request body to `listSales`.",
63289
- "operationId": "countSales",
63290
- "requestBody": {
63291
- "content": {
63292
- "application/json": {
63293
- "schema": {
63294
- "$ref": "#/components/schemas/SaleListRequest"
63295
- }
64361
+ "/v1/saved-filters/{id}": {
64362
+ "delete": {
64363
+ "operationId": "deleteSavedFilter",
64364
+ "parameters": [
64365
+ {
64366
+ "description": "Saved-filter id (nanoid).",
64367
+ "in": "path",
64368
+ "name": "id",
64369
+ "required": true,
64370
+ "schema": {
64371
+ "description": "Saved-filter id (nanoid).",
64372
+ "example": "V1StGXR8_Z5jdHi6B-myT",
64373
+ "maxLength": 32,
64374
+ "minLength": 1,
64375
+ "pattern": "^[\\w-]+$",
64376
+ "type": "string"
63296
64377
  }
63297
64378
  }
63298
- },
64379
+ ],
63299
64380
  "responses": {
63300
64381
  "200": {
63301
64382
  "content": {
63302
64383
  "application/json": {
63303
64384
  "schema": {
63304
- "$ref": "#/components/schemas/CountResponse"
64385
+ "$ref": "#/components/schemas/DeleteSavedFilterResult"
63305
64386
  }
63306
64387
  }
63307
64388
  },
63308
- "description": "Exact count of matching sales"
64389
+ "description": "Deleted."
63309
64390
  },
63310
64391
  "400": {
63311
64392
  "content": {
@@ -63341,11 +64422,11 @@
63341
64422
  "content": {
63342
64423
  "application/json": {
63343
64424
  "schema": {
63344
- "$ref": "#/components/schemas/ErrorResponse"
64425
+ "$ref": "#/components/schemas/SavedFilterError"
63345
64426
  }
63346
64427
  }
63347
64428
  },
63348
- "description": "Not found"
64429
+ "description": "No saved filter with that id belongs to the caller."
63349
64430
  },
63350
64431
  "413": {
63351
64432
  "content": {
@@ -63358,24 +64439,26 @@
63358
64439
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63359
64440
  }
63360
64441
  },
63361
- "summary": "Count sales matching filters",
64442
+ "summary": "Delete a saved filter",
63362
64443
  "tags": [
63363
- "Sales"
63364
- ],
63365
- "x-mm-records-via": "listSales"
63366
- }
63367
- },
63368
- "/v1/sales/{id}": {
64444
+ "Saved Filters"
64445
+ ]
64446
+ },
63369
64447
  "get": {
63370
- "operationId": "getSale",
64448
+ "description": "Returns the saved `query` and the `operationId` to run it with. Check `fidelity` (`partial` → `unmapped` lists what the query omits; `ui_only` → nothing to run) and `stale` (the query no longer validates) before running.",
64449
+ "operationId": "getSavedFilter",
63371
64450
  "parameters": [
63372
64451
  {
63373
- "description": "Sale document ID",
64452
+ "description": "Saved-filter id (nanoid).",
63374
64453
  "in": "path",
63375
64454
  "name": "id",
63376
64455
  "required": true,
63377
64456
  "schema": {
63378
- "description": "Sale document ID",
64457
+ "description": "Saved-filter id (nanoid).",
64458
+ "example": "V1StGXR8_Z5jdHi6B-myT",
64459
+ "maxLength": 32,
64460
+ "minLength": 1,
64461
+ "pattern": "^[\\w-]+$",
63379
64462
  "type": "string"
63380
64463
  }
63381
64464
  }
@@ -63385,13 +64468,13 @@
63385
64468
  "content": {
63386
64469
  "application/json": {
63387
64470
  "schema": {
63388
- "$ref": "#/components/schemas/SaleDetailResponse"
64471
+ "$ref": "#/components/schemas/GetSavedFilterResult"
63389
64472
  }
63390
64473
  }
63391
64474
  },
63392
- "description": "Sale detail"
64475
+ "description": "The saved filter."
63393
64476
  },
63394
- "404": {
64477
+ "400": {
63395
64478
  "content": {
63396
64479
  "application/json": {
63397
64480
  "schema": {
@@ -63399,7 +64482,37 @@
63399
64482
  }
63400
64483
  }
63401
64484
  },
63402
- "description": "Sale not found"
64485
+ "description": "Validation error"
64486
+ },
64487
+ "401": {
64488
+ "content": {
64489
+ "application/json": {
64490
+ "schema": {
64491
+ "$ref": "#/components/schemas/ErrorResponse"
64492
+ }
64493
+ }
64494
+ },
64495
+ "description": "Unauthorized"
64496
+ },
64497
+ "403": {
64498
+ "content": {
64499
+ "application/json": {
64500
+ "schema": {
64501
+ "$ref": "#/components/schemas/ErrorResponse"
64502
+ }
64503
+ }
64504
+ },
64505
+ "description": "Forbidden (authenticated, but missing required context — e.g. no active organization)"
64506
+ },
64507
+ "404": {
64508
+ "content": {
64509
+ "application/json": {
64510
+ "schema": {
64511
+ "$ref": "#/components/schemas/SavedFilterError"
64512
+ }
64513
+ }
64514
+ },
64515
+ "description": "No saved filter with that id belongs to the caller."
63403
64516
  },
63404
64517
  "413": {
63405
64518
  "content": {
@@ -63409,78 +64522,62 @@
63409
64522
  }
63410
64523
  }
63411
64524
  },
63412
- "description": "Response payload too large"
64525
+ "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63413
64526
  }
63414
64527
  },
63415
- "summary": "Get sale by ID",
64528
+ "summary": "Get a saved filter (with the operation to run it)",
63416
64529
  "tags": [
63417
- "Sales"
64530
+ "Saved Filters"
63418
64531
  ]
63419
- }
63420
- },
63421
- "/v1/sales/{id}/related": {
63422
- "get": {
63423
- "description": "Return the ids of records of another type that are linked to this record. Links resolve by identifier, strongest identifier first, and equally-trusted identifiers are combined — a company's linked loans cover every role it holds on a loan, not just the first one found. The response reports which strength was used: `primary` is the canonical identifier for that pair, `secondary` is a real but weaker alternate used only when the record carries nothing better. This is a lightweight index: it returns ids and a count, not full records. For filtering, sorting, pagination and full detail, use the named endpoint for the pair (for example the originator loans endpoint) and fetch records by id.",
63424
- "operationId": "saleRelated",
64532
+ },
64533
+ "patch": {
64534
+ "description": "Rename, re-icon, or replace the query / UI state. The target is fixed. A new `query` is validated like on create.",
64535
+ "operationId": "updateSavedFilter",
63425
64536
  "parameters": [
63426
64537
  {
63427
- "description": "Sale id",
64538
+ "description": "Saved-filter id (nanoid).",
63428
64539
  "in": "path",
63429
64540
  "name": "id",
63430
64541
  "required": true,
63431
64542
  "schema": {
63432
- "description": "Sale id",
63433
- "type": "string"
63434
- }
63435
- },
63436
- {
63437
- "description": "Type of record to link to",
63438
- "in": "query",
63439
- "name": "to",
63440
- "required": true,
63441
- "schema": {
63442
- "description": "Type of record to link to",
63443
- "enum": [
63444
- "agents",
63445
- "loans",
63446
- "properties"
63447
- ],
64543
+ "description": "Saved-filter id (nanoid).",
64544
+ "example": "V1StGXR8_Z5jdHi6B-myT",
64545
+ "maxLength": 32,
64546
+ "minLength": 1,
64547
+ "pattern": "^[\\w-]+$",
63448
64548
  "type": "string"
63449
64549
  }
63450
- },
63451
- {
63452
- "description": "Maximum ids to return (default 25, max 200)",
63453
- "in": "query",
63454
- "name": "limit",
63455
- "required": false,
63456
- "schema": {
63457
- "description": "Maximum ids to return (default 25, max 200)",
63458
- "maximum": 200,
63459
- "minimum": 1,
63460
- "type": "integer"
63461
- }
63462
64550
  }
63463
64551
  ],
64552
+ "requestBody": {
64553
+ "content": {
64554
+ "application/json": {
64555
+ "schema": {
64556
+ "$ref": "#/components/schemas/UpdateSavedFilterRequest"
64557
+ }
64558
+ }
64559
+ }
64560
+ },
63464
64561
  "responses": {
63465
64562
  "200": {
63466
64563
  "content": {
63467
64564
  "application/json": {
63468
64565
  "schema": {
63469
- "$ref": "#/components/schemas/RelatedResponse"
64566
+ "$ref": "#/components/schemas/UpdateSavedFilterResult"
63470
64567
  }
63471
64568
  }
63472
64569
  },
63473
- "description": "Linked record ids"
64570
+ "description": "The updated filter."
63474
64571
  },
63475
64572
  "400": {
63476
64573
  "content": {
63477
64574
  "application/json": {
63478
64575
  "schema": {
63479
- "$ref": "#/components/schemas/ErrorResponse"
64576
+ "$ref": "#/components/schemas/SavedFilterError"
63480
64577
  }
63481
64578
  }
63482
64579
  },
63483
- "description": "Validation error"
64580
+ "description": "Validation error — including a `query` that is not a valid request for the target's operation (`issues` lists each problem)."
63484
64581
  },
63485
64582
  "401": {
63486
64583
  "content": {
@@ -63506,11 +64603,11 @@
63506
64603
  "content": {
63507
64604
  "application/json": {
63508
64605
  "schema": {
63509
- "$ref": "#/components/schemas/ErrorResponse"
64606
+ "$ref": "#/components/schemas/SavedFilterError"
63510
64607
  }
63511
64608
  }
63512
64609
  },
63513
- "description": "Record not found"
64610
+ "description": "No saved filter with that id belongs to the caller."
63514
64611
  },
63515
64612
  "413": {
63516
64613
  "content": {
@@ -63523,9 +64620,9 @@
63523
64620
  "description": "Response payload too large — detail endpoint capped to fit Lambda response limits"
63524
64621
  }
63525
64622
  },
63526
- "summary": "Records linked to this one",
64623
+ "summary": "Update a saved filter",
63527
64624
  "tags": [
63528
- "Related"
64625
+ "Saved Filters"
63529
64626
  ]
63530
64627
  }
63531
64628
  },
@@ -63689,6 +64786,10 @@
63689
64786
  "description": "Real-estate sales transactions",
63690
64787
  "name": "Sales"
63691
64788
  },
64789
+ {
64790
+ "description": "Your saved list filters — store a query once, re-run it against its list operation",
64791
+ "name": "Saved Filters"
64792
+ },
63692
64793
  {
63693
64794
  "description": "Instant multi-entity search",
63694
64795
  "name": "Search"