API Documentation
Saved Searches ¶
Overview
A Saved Search stores a set of listing-search criteria (price range, bedroom count, property types, geographic boundaries, etc.) tied to a brand. Saved searches can be linked to contacts (with a delivery frequency) and to templates (per email-topic) so that newly-matching listings can be delivered as alerts. They can also be linked to boundaries — geographic polygons that the listing must fall within.
Identity and ownership
| Field | Type | Association | Description |
|---|---|---|---|
| id | uuid | Internal identifier of the saved search | |
| brand | uuid | brands(id) | Brand to which this saved search belongs |
| created_by | uuid | users(id) | User who created the search (server-controlled; nullable for portal-created searches) |
| from | uuid | users(id) | Sender associated with notifications (server-controlled) |
| title | string | Human-readable title | |
| created_at | number | Epoch seconds when created | |
| updated_at | number | Epoch seconds of last update | |
| deleted_at | number | Epoch seconds of soft-delete (nullable) |
Price, area, room counts, year built, parking
| Field | Type | Description |
|---|---|---|
| minimum_price | number | Lower bound of price range |
| maximum_price | number | Upper bound of price range |
| currency | string | ISO 4217 code; defaults to USD |
| minimum_square_meters | number | Lower bound of interior area in m² |
| maximum_square_meters | number | Upper bound of interior area in m² |
| minimum_lot_square_meters | number | Lower bound of lot area in m² |
| maximum_lot_square_meters | number | Upper bound of lot area in m² |
| minimum_bedrooms | number | Lower bound, integer |
| maximum_bedrooms | number | Upper bound, integer |
| minimum_bathrooms | number | Lower bound (fractional bathrooms permitted) |
| maximum_bathrooms | number | Upper bound, integer |
| minimum_year_built | number | Lower bound, year |
| maximum_year_built | number | Upper bound, year |
| minimum_parking_spaces | number | Minimum number of covered parking spaces |
Property types, statuses, architectural styles
| Field | Type | Description |
|---|---|---|
| property_types | string[] | One or more property_type enum values (e.g. Residential, Commercial) |
| property_subtypes | string[] | One or more property_subtype enum values (e.g. RES-Single Family) |
| listing_statuses | string[] | One or more listing_status enum values to include |
| architectural_styles | string[] | Free-form architectural style tags; matched as a set-overlap |
Geographic filters
| Field | Type | Description |
|---|---|---|
| points | object[] | Polygon vertices {latitude, longitude} defining a custom area; closes the ring |
| counties | string[] | County / parish names to include |
| subdivisions | string[] | Subdivision names to include |
| postal_codes | string[] | ZIP / postal codes to include |
Boundaries are not a column on saved_searches — they live in the
saved_searches_boundaries join table and are managed via the create /
update payload (see “Boundaries” below).
School filters
Each is an array of school names; a listing matches if its corresponding field is in the alert’s array.
| Field | Type |
|---|---|
| school_districts | string[] |
| primary_schools | string[] |
| middle_schools | string[] |
| elementary_schools | string[] |
| senior_high_schools | string[] |
| junior_high_schools | string[] |
| intermediate_schools | string[] |
| high_schools | string[] |
Agent and office filters
| Field | Type | Description |
|---|---|---|
| list_agents | string[] | Match the listing’s list_agent_mui |
| selling_agents | string[] | Match the listing’s selling_agent_mui |
| agents | string[] | Match any of the listing’s 8 agent slots (list, co-list ×3, selling, co-selling ×3) |
| list_offices | string[] | Match the listing’s list_office_mls_id |
| selling_offices | string[] | Match the listing’s selling_office_mls_id |
| offices | string[] | Match any of the listing’s 4 office slots (list, co-list, selling, co-selling) |
Boolean amenities
For pool, pets, application_fee, appliances, furnished,
fenced_yard: passing true requires the listing’s flag to be true;
passing false requires it to be false or NULL (treated as “not
present”). Passing null (or omitting) skips the filter.
| Field | Type |
|---|---|
| pool | boolean |
| pets | boolean |
| application_fee | boolean |
| appliances | boolean |
| furnished | boolean |
| fenced_yard | boolean |
office_exclusive is checked strictly — true/false must equal the
listing’s value (no NULL fallback).
| Field | Type |
|---|---|
| office_exclusive | boolean |
Pets and miscellaneous
| Field | Type | Description |
|---|---|---|
| number_of_pets_allowed | number | Lower bound — listing’s number_of_pets_allowed must be at least this |
| open_house | boolean | When true, only listings with an active open-house match |
| master_bedroom_in_first_floor | boolean | When true, only listings whose master bedroom is on level 1 match |
| minimum_sold_date | number | Epoch seconds; only constrains Sold listings — non-Sold rows pass |
Full-text search
| Field | Type | Description |
|---|---|---|
| search | string | Free-text query matched via search_listings(websearch_to_tsquery('english', search)) |
| content | string | Free-text matched against the listing’s content column via plainto_tsquery('english', content) |
| address | string | Free-text matched against the listing’s address column via plainto_tsquery('english', address) |
MLS areas
mls_areas is a jsonb array of [major, minor] integer pairs. A pair
[X, 0] matches every sub-area of major area X; [X, Y] matches the
specific minor Y under major X.
| Field | Type | Description |
|---|---|---|
| mls_areas | jsonb | Array of [major, minor] integer pairs |
Linked resources
A saved search can link to three side tables. All three accept the linked
arrays in the POST body; only boundaries is also accepted on PUT.
Contacts (saved_searches_contacts)
Created via the portal-token flow only (POST /portals/:id/saved-searches).
Each link carries a delivery frequency:
| Field | Type | Description |
|---|---|---|
| contact | uuid | Contact ID; must belong to the saved search’s brand |
| frequency | string | Delivery frequency — one of Instant, Daily, Weekly, Monthly, Never |
Templates (saved_searches_templates)
Each link associates one template with one topic; a saved search may have at most one template per topic.
| Field | Type | Description |
|---|---|---|
| template | uuid | Template ID |
| topic | string | One of NewListings, OpenHouse, PriceImprovement, Sold |
Boundaries (saved_searches_boundaries)
Pure list of boundary IDs; the listing’s location must be within the
union of all linked boundary geometries.
| Field | Type | Description |
|---|---|---|
| boundary | uuid | Boundary ID (from boundaries table) |
Endpoints
Create a saved search ¶
Create a saved searchPOST/brands/:brand/saved-searches
brand (uuid, required) — ID of the brand that owns the search; the
authenticated user must have access. Hardcoded server-side from the URL —
ignored if also present in the body.
created_by is server-set to req.user.id. from defaults to
req.user.id if not provided.
Body accepts any of the fields documented above. boundaries is an array
of boundary UUIDs; passing one creates rows in saved_searches_boundaries
in the same transaction.
contacts and templates linkage is not exposed via this endpoint —
use the portal-token endpoints for that.
Example URI
- brand
string(required) Example: ed24b242-2d6d-4dc1-ac18-f2fe826271ad
Body
{
"title": "My Search",
"minimum_price": 100000,
"maximum_price": 500000,
"minimum_bedrooms": 2,
"property_types": [
"Residential"
],
"listing_statuses": [
"Active"
],
"currency": "USD",
"points": [
{
"latitude": -89,
"longitude": -179
},
{
"latitude": -89,
"longitude": 179
},
{
"latitude": 89,
"longitude": 179
},
{
"latitude": 89,
"longitude": -179
},
{
"latitude": -89,
"longitude": -179
}
]
}200Body
{
"code": "OK",
"data": {
"type": "saved_search",
"id": "c1ca0503-0eba-44d6-8f80-7517e35cb03c",
"title": "My Search",
"minimum_price": 100000,
"maximum_price": 500000,
"currency": "USD",
"minimum_square_meters": null,
"maximum_square_meters": null,
"minimum_lot_square_meters": null,
"maximum_lot_square_meters": null,
"minimum_bedrooms": 2,
"maximum_bedrooms": null,
"minimum_bathrooms": null,
"maximum_bathrooms": null,
"minimum_year_built": null,
"maximum_year_built": null,
"minimum_parking_spaces": null,
"property_types": [
"Residential"
],
"property_subtypes": null,
"listing_statuses": [
"Active"
],
"architectural_styles": null,
"points": [
{
"longitude": -179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": -89,
"type": "location"
}
],
"mls_areas": null,
"counties": null,
"subdivisions": null,
"postal_codes": null,
"school_districts": null,
"primary_schools": null,
"middle_schools": null,
"elementary_schools": null,
"senior_high_schools": null,
"junior_high_schools": null,
"intermediate_schools": null,
"high_schools": null,
"list_agents": null,
"list_offices": null,
"selling_agents": null,
"selling_offices": null,
"agents": null,
"offices": null,
"pool": null,
"open_house": null,
"pets": null,
"number_of_pets_allowed": null,
"application_fee": null,
"appliances": null,
"furnished": null,
"fenced_yard": null,
"office_exclusive": null,
"master_bedroom_in_first_floor": false,
"minimum_sold_date": null,
"search": null,
"created_at": 1778872298.140854,
"updated_at": 1778872298.140854,
"deleted_at": null,
"proposed_title": "Active, $100K-$500K, 2+ Beds"
}
}List a brand's saved searches ¶
List a brand's saved searchesGET/brands/:brand/saved-searches
brand (uuid, required) — ID of the brand.
Returns every non-deleted saved search for the brand, newest first.
Example URI
- brand
string(required) Example: ed24b242-2d6d-4dc1-ac18-f2fe826271ad
200Body
{
"code": "OK",
"data": [
{
"type": "saved_search",
"id": "c1ca0503-0eba-44d6-8f80-7517e35cb03c",
"title": "My Search",
"minimum_price": 100000,
"maximum_price": 500000,
"currency": "USD",
"minimum_square_meters": null,
"maximum_square_meters": null,
"minimum_lot_square_meters": null,
"maximum_lot_square_meters": null,
"minimum_bedrooms": 2,
"maximum_bedrooms": null,
"minimum_bathrooms": null,
"maximum_bathrooms": null,
"minimum_year_built": null,
"maximum_year_built": null,
"minimum_parking_spaces": null,
"property_types": [
"Residential"
],
"property_subtypes": null,
"listing_statuses": [
"Active"
],
"architectural_styles": null,
"points": [
{
"longitude": -179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": -89,
"type": "location"
}
],
"mls_areas": null,
"counties": null,
"subdivisions": null,
"postal_codes": null,
"school_districts": null,
"primary_schools": null,
"middle_schools": null,
"elementary_schools": null,
"senior_high_schools": null,
"junior_high_schools": null,
"intermediate_schools": null,
"high_schools": null,
"list_agents": null,
"list_offices": null,
"selling_agents": null,
"selling_offices": null,
"agents": null,
"offices": null,
"pool": null,
"open_house": null,
"pets": null,
"number_of_pets_allowed": null,
"application_fee": null,
"appliances": null,
"furnished": null,
"fenced_yard": null,
"office_exclusive": null,
"master_bedroom_in_first_floor": false,
"minimum_sold_date": null,
"search": null,
"created_at": 1778872298.140854,
"updated_at": 1778872298.140854,
"deleted_at": null,
"proposed_title": "Active, $100K-$500K, 2+ Beds"
}
],
"info": {
"count": 1,
"total": 0
}
}Get a saved search by id ¶
Get a saved search by idGET/brands/:brand/saved-searches/:id
brand (uuid, required) — ID of the brand. Must match the saved search’s
brand or the request fails (HTTP 400 Validation).
id (uuid, required) — ID of the saved search.
Example URI
- brand
string(required) Example: ed24b242-2d6d-4dc1-ac18-f2fe826271ad- id
string(required) Example: c1ca0503-0eba-44d6-8f80-7517e35cb03c
200Body
{
"code": "OK",
"data": {
"type": "saved_search",
"id": "c1ca0503-0eba-44d6-8f80-7517e35cb03c",
"title": "My Search",
"minimum_price": 100000,
"maximum_price": 500000,
"currency": "USD",
"minimum_square_meters": null,
"maximum_square_meters": null,
"minimum_lot_square_meters": null,
"maximum_lot_square_meters": null,
"minimum_bedrooms": 2,
"maximum_bedrooms": null,
"minimum_bathrooms": null,
"maximum_bathrooms": null,
"minimum_year_built": null,
"maximum_year_built": null,
"minimum_parking_spaces": null,
"property_types": [
"Residential"
],
"property_subtypes": null,
"listing_statuses": [
"Active"
],
"architectural_styles": null,
"points": [
{
"longitude": -179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": -89,
"type": "location"
}
],
"mls_areas": null,
"counties": null,
"subdivisions": null,
"postal_codes": null,
"school_districts": null,
"primary_schools": null,
"middle_schools": null,
"elementary_schools": null,
"senior_high_schools": null,
"junior_high_schools": null,
"intermediate_schools": null,
"high_schools": null,
"list_agents": null,
"list_offices": null,
"selling_agents": null,
"selling_offices": null,
"agents": null,
"offices": null,
"pool": null,
"open_house": null,
"pets": null,
"number_of_pets_allowed": null,
"application_fee": null,
"appliances": null,
"furnished": null,
"fenced_yard": null,
"office_exclusive": null,
"master_bedroom_in_first_floor": false,
"minimum_sold_date": null,
"search": null,
"created_at": 1778872298.140854,
"updated_at": 1778872298.140854,
"deleted_at": null,
"proposed_title": "Active, $100K-$500K, 2+ Beds"
}
}Update a saved search ¶
Update a saved searchPUT/brands/:brand/saved-searches/:id
brand (uuid, required) — ID of the brand. Must match the saved search’s
brand or the request fails (HTTP 400 Validation).
id (uuid, required) — ID of the saved search.
Accepts a partial update — fields not present in the body retain their
existing values. boundaries has special semantics:
-
Omitted — existing linked boundaries are left untouched.
-
[]— all linked boundaries are removed. -
[uuid, …]— replace the linked set with exactly the given UUIDs.
brand, created_by are immutable after creation.
Example URI
- brand
string(required) Example: ed24b242-2d6d-4dc1-ac18-f2fe826271ad- id
string(required) Example: c1ca0503-0eba-44d6-8f80-7517e35cb03c
Body
{
"title": "Updated Search",
"maximum_price": 750000
}200Body
{
"code": "OK",
"data": {
"type": "saved_search",
"id": "c1ca0503-0eba-44d6-8f80-7517e35cb03c",
"title": "Updated Search",
"minimum_price": 100000,
"maximum_price": 750000,
"currency": "USD",
"minimum_square_meters": null,
"maximum_square_meters": null,
"minimum_lot_square_meters": null,
"maximum_lot_square_meters": null,
"minimum_bedrooms": 2,
"maximum_bedrooms": null,
"minimum_bathrooms": null,
"maximum_bathrooms": null,
"minimum_year_built": null,
"maximum_year_built": null,
"minimum_parking_spaces": null,
"property_types": [
"Residential"
],
"property_subtypes": null,
"listing_statuses": [
"Active"
],
"architectural_styles": null,
"points": [
{
"longitude": -179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": -89,
"type": "location"
},
{
"longitude": 179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": 89,
"type": "location"
},
{
"longitude": -179,
"latitude": -89,
"type": "location"
}
],
"mls_areas": null,
"counties": null,
"subdivisions": null,
"postal_codes": null,
"school_districts": null,
"primary_schools": null,
"middle_schools": null,
"elementary_schools": null,
"senior_high_schools": null,
"junior_high_schools": null,
"intermediate_schools": null,
"high_schools": null,
"list_agents": null,
"list_offices": null,
"selling_agents": null,
"selling_offices": null,
"agents": null,
"offices": null,
"pool": null,
"open_house": null,
"pets": null,
"number_of_pets_allowed": null,
"application_fee": null,
"appliances": null,
"furnished": null,
"fenced_yard": null,
"office_exclusive": null,
"master_bedroom_in_first_floor": false,
"minimum_sold_date": null,
"search": null,
"created_at": 1778872298.140854,
"updated_at": 1778872298.254139,
"deleted_at": null,
"proposed_title": "Active, $100K-$750K, 2+ Beds"
}
}Delete a saved search ¶
Delete a saved searchDELETE/brands/:brand/saved-searches/:id
brand (uuid, required) — ID of the brand. Must match the saved search’s
brand or the request fails (HTTP 400 Validation).
id (uuid, required) — ID of the saved search.
Soft-deletes the saved search by setting deleted_at. Linked rows in
saved_searches_contacts, saved_searches_templates, and
saved_searches_boundaries are not cascaded — they remain in place
with their FK pointing at the soft-deleted row.
Responds 204 No Content.
Example URI
- brand
string(required) Example: ed24b242-2d6d-4dc1-ac18-f2fe826271ad- id
string(required) Example: c1ca0503-0eba-44d6-8f80-7517e35cb03c
204