Searches
What a client needs, in structure or in their words, versioned as you change it. Served at `/api/v1/searches`. The old `/api/v1/briefs` paths still answer, and say they are old.
List searches
/api/v1/searchesYour searches, each at its latest revision, newest saved first. Archived searches stay out unless you ask. Pages by cursor, 20 at a time and up to 50.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | Query | "active" | "archived" | "all" | Optional | active (the default), archived or all. It stays on the next page's link. |
limit | Query | integer | Optional | How many to return: 1 to 50, 20 when absent. A number outside that range is clamped; text is ignored. |
cursor | Query | string | Optional | Where the last page ended: the cursor inside the previous page's links.next. Pass links as given; a cursor you edit is refused. |
Examples
curl "https://liviamatch.com/api/v1/searches" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
One page of searches.
{
"data": [
{
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches",
"runs": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/runs"
}
}
],
"links": {
"next": null
}
}Fields (75)
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].id | string (uuid) | |
data[].version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data[].title | string | |
data[].clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data[].source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data[].tenure | "buy" | "rent" | |
data[].budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data[].budget.value | number | The target. |
data[].budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].beds | object | The fewest bedrooms. |
data[].beds.value | number | The target. |
data[].beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].baths | object | The fewest bathrooms. |
data[].baths.value | number | The target. |
data[].baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data[].timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data[].areas | string[] | The cities to look in. |
data[].commutes | object[] | |
data[].commutes[].id | string | 1 to 64 characters |
data[].commutes[].label | string | at most 200 characters |
data[].commutes[].coords | object | null | |
data[].commutes[].coords.lat | number | |
data[].commutes[].coords.lng | number | |
data[].commutes[].maxMin | integer | 0 to 60 |
data[].commutes[].kind | "place" | "any", optional | |
data[].commutes[].query | string, optional | 1 to 80 characters |
data[].areaPins | object[] | |
data[].areaPins[].id | string | 1 to 64 characters |
data[].areaPins[].lat | number | |
data[].areaPins[].lng | number | |
data[].mustHaves | string[] | |
data[].niceToHaves | string[] | |
data[].details | object | null | Advanced criteria, or null when none were set. |
data[].details.text | string | at most 4000 characters, default "" |
data[].details.textMode | "prefer" | "require" | default "prefer" |
data[].details.numeric | object | default {} |
data[].details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data[].details.cities | string[] | at most 100 items, default [] |
data[].details.postalCodes | string[] | at most 100 items, default [] |
data[].details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data[].details.requiredFeatures | string[] | at most 50 items, default [] |
data[].details.preferredFeatures | string[] | at most 50 items, default [] |
data[].details.excludedFeatures | string[] | at most 50 items, default [] |
data[].details.imageDescription | string | at most 2000 characters, default "" |
data[].details.imageTags | string[] | at most 50 items, default [] |
data[].details.radius | object, optional | |
data[].details.radius.latitude | number | -90 to 90 |
data[].details.radius.longitude | number | -180 to 180 |
data[].details.radius.km | number | at most 2000 |
data[].details.area | object | default {} |
data[].details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data[].details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data[].likedHomes | object | |
data[].likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data[].likedHomes.importance | "nice" | "must" | default "nice" |
data[].feel | object | |
data[].feel.text | string | at most 2000 characters, default "" |
data[].feel.tags | string[] | at most 12 items, default [] |
data[].feel.importance | "nice" | "must" | default "nice" |
data[].inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data[].inferred.fields | string[] | at most 40 items |
data[].inferred.assumed | string[] | at most 10 items |
data[].archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data[].updatedAt | string (date-time) | When this revision was saved. |
data[].links | object | |
data[].links.self | string | |
data[].links.matches | string | The old path of runs, which still answers. |
data[].links.runs | string | Where to POST to run the search ("Search now"), and GET to list its runs. |
links | object | |
links.next | string | null | The URL of the next page, or null when this is the last. Follow it as given. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The status is not active, archived or all. |
| 400 | invalid-cursor | The cursor is not one a page linked to. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Make a search
/api/v1/searchesMakes a search from structure and answers 201 with it, its ETag and a Location. The server picks the id.
Budget, beds and baths are required, so a search is never made of starting values. Everything else has an honest default: no preference, an empty list. To start from words, draft one and send what it returned.
A POST that times out may or may not have landed. To make a retry safe, choose the id yourself and use PUT /api/v1/searches/{searchId}: the same id with the same words lands once.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Request body
The whole search. Unknown fields are refused, so a typo fails loudly.
Fields (64)
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | What you call this brief: 1 to 120 characters.1 to 120 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
source | string | null | Optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
tenure | "buy" | "rent" | Optional | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
budget | object | Required | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
budget.value | number | Required | at most 100000000 |
budget.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
budget.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
beds | object | Required | The fewest bedrooms. Firm unless you say otherwise. |
beds.value | integer | Required | 0 to 30 |
beds.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
beds.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
baths | object | Required | The fewest bathrooms. Firm unless you say otherwise. |
baths.value | number | Required | 0 to 30 |
baths.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
baths.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | Optional | default "Any" |
timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | Optional | default "Exploring" |
areas | string[] | Optional | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
commutes | object[] | Optional | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
commutes[].id | string | Optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
commutes[].label | string | Required | 1 to 200 characters |
commutes[].coords | object | null | Required | Where the stop is. Null when kind is any: the match finds the places. |
commutes[].coords.lat | number | Required | -90 to 90 |
commutes[].coords.lng | number | Required | -180 to 180 |
commutes[].maxMin | integer | Required | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
commutes[].kind | "place" | "any" | Optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
commutes[].query | string | Optional | The brand, when kind is any.1 to 80 characters |
areaPins | object[] | Optional | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
areaPins[].id | string | Optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
areaPins[].lat | number | Required | -90 to 90 |
areaPins[].lng | number | Required | -180 to 180 |
mustHaves | string[] | Optional | at most 20 items, default [] |
niceToHaves | string[] | Optional | at most 20 items, default [] |
details | object | null | Optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
details.text | string | Optional | at most 4000 characters, default "" |
details.textMode | "prefer" | "require" | Optional | default "prefer" |
details.numeric | object | Optional | default {} |
details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | Optional | at most 7 items, default [] |
details.cities | string[] | Optional | at most 100 items, default [] |
details.postalCodes | string[] | Optional | at most 100 items, default [] |
details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | Optional | at least 1 items, default ["Active"] |
details.requiredFeatures | string[] | Optional | at most 50 items, default [] |
details.preferredFeatures | string[] | Optional | at most 50 items, default [] |
details.excludedFeatures | string[] | Optional | at most 50 items, default [] |
details.imageDescription | string | Optional | at most 2000 characters, default "" |
details.imageTags | string[] | Optional | at most 50 items, default [] |
details.radius | object | Optional | |
details.radius.latitude | number | Required | -90 to 90 |
details.radius.longitude | number | Required | -180 to 180 |
details.radius.km | number | Required | at most 2000 |
details.area | object | Optional | default {} |
details.maxEvidenceAgeDays | integer | Optional | at most 3650, default 365 |
details.missingData | "exclude-required" | "penalize" | Optional | default "exclude-required" |
likedHomes | object | Optional | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
likedHomes.urls | string (uri)[] | Optional | at most 4 items, default [] |
likedHomes.importance | "nice" | "must" | Optional | default "nice" |
feel | object | Optional | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
feel.text | string | Optional | at most 2000 characters, default "" |
feel.tags | string[] | Optional | at most 12 items, default [] |
feel.importance | "nice" | "must" | Optional | default "nice" |
inferred | object | null | Optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
inferred.fields | string[] | Required | at most 40 items |
inferred.assumed | string[] | Required | at most 10 items |
{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 750000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}Examples
curl -X POST "https://liviamatch.com/api/v1/searches" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 750000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}'Responses
The search was made, at revision 1.
HeadersETagLocation
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches",
"runs": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/runs"
}
}
}Fields (73)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | The old path of runs, which still answers. |
data.links.runs | string | Where to POST to run the search ("Search now"), and GET to list its runs. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | source-unavailable | source is not Homes for sale and not an active data set of yours (GET /api/v1/data-sets). |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Draft a search from words
/api/v1/searches/draftReads notes about a client (a consultation, an email, a dictation) and answers the search they describe, in the shape POST /api/v1/searches takes. It saves nothing: look at it, change what is wrong, then send it.
search.inferred (brief.inferred on the old path) names what the words said (fields) and the essentials they never mentioned (assumed). Those are budget and propertyType, each left at the value a new search starts with. A search drafted before 2026-10-22 may also list beds and baths. Beds and baths the words never named are now drafted with no floor (value 0). A field that claims nothing, like that or timeline at Exploring, is not an assumption and is never listed. Pass inferred back when you save, so the assumptions stay named on the search, and set the assumed essentials yourself when you know them.
The words go to the model under the no-retention routing rule, pass the Fair Housing scrub before and after, and are never stored or logged. A draft counts against an hourly cap, and words that name nothing a search can use are refused rather than drafted from starting values.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Request body
The notes to read, and the client they are about when there is one.
Fields (2)
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Required | What the agent heard or wrote about the client: a consultation, an email, notes. Up to 10,000 characters. It goes to the model under the no-retention routing rule and is never stored.1 to 10000 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null. It rides on the draft so it can be saved as it is.default null |
{
"text": "Amara and her husband are moving from Boston in January with two kids and a dog. Budget is around $750k, three bedrooms at least, two baths, somewhere in Nashville near Vanderbilt hospital. A fenced yard is a must, and a home office would be nice.",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28"
}Examples
curl -X POST "https://liviamatch.com/api/v1/searches/draft" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Amara and her husband are moving from Boston in January with two kids and a dog. Budget is around $750k, three bedrooms at least, two baths, somewhere in Nashville near Vanderbilt hospital. A fenced yard is a must, and a home office would be nice.",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28"
}'Responses
A draft, ready to look at and save.
{
"data": {
"brief": {
"title": "Boston family, near Vanderbilt",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [],
"areaPins": [],
"mustHaves": [
"Fenced yard"
],
"niceToHaves": [
"Home office"
],
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": {
"fields": [
"title",
"budget",
"beds",
"baths",
"cities",
"feature"
],
"assumed": [
"propertyType"
]
}
},
"search": {
"title": "Boston family, near Vanderbilt",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [],
"areaPins": [],
"mustHaves": [
"Fenced yard"
],
"niceToHaves": [
"Home office"
],
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": {
"fields": [
"title",
"budget",
"beds",
"baths",
"cities",
"feature"
],
"assumed": [
"propertyType"
]
}
},
"setAside": []
}
}Fields (132)
| Field | Type | Description |
|---|---|---|
data | object | |
data.brief | object | The old name for search, with the same body. |
data.brief.title | string | What you call this brief: 1 to 120 characters.1 to 120 characters |
data.brief.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
data.brief.source | string | null, optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
data.brief.tenure | "buy" | "rent" | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
data.brief.budget | object | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
data.brief.budget.value | number | at most 100000000 |
data.brief.budget.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
data.brief.budget.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
data.brief.beds | object | The fewest bedrooms. Firm unless you say otherwise. |
data.brief.beds.value | integer | 0 to 30 |
data.brief.beds.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.brief.beds.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.brief.baths | object | The fewest bathrooms. Firm unless you say otherwise. |
data.brief.baths.value | number | 0 to 30 |
data.brief.baths.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.brief.baths.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.brief.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | default "Any" |
data.brief.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | default "Exploring" |
data.brief.areas | string[] | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
data.brief.commutes | object[] | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
data.brief.commutes[].id | string, optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
data.brief.commutes[].label | string | 1 to 200 characters |
data.brief.commutes[].coords | object | null | Where the stop is. Null when kind is any: the match finds the places. |
data.brief.commutes[].coords.lat | number | -90 to 90 |
data.brief.commutes[].coords.lng | number | -180 to 180 |
data.brief.commutes[].maxMin | integer | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
data.brief.commutes[].kind | "place" | "any", optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
data.brief.commutes[].query | string, optional | The brand, when kind is any.1 to 80 characters |
data.brief.areaPins | object[] | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
data.brief.areaPins[].id | string, optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
data.brief.areaPins[].lat | number | -90 to 90 |
data.brief.areaPins[].lng | number | -180 to 180 |
data.brief.mustHaves | string[] | at most 20 items, default [] |
data.brief.niceToHaves | string[] | at most 20 items, default [] |
data.brief.details | object | null, optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
data.brief.details.text | string | at most 4000 characters, default "" |
data.brief.details.textMode | "prefer" | "require" | default "prefer" |
data.brief.details.numeric | object | default {} |
data.brief.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.brief.details.cities | string[] | at most 100 items, default [] |
data.brief.details.postalCodes | string[] | at most 100 items, default [] |
data.brief.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.brief.details.requiredFeatures | string[] | at most 50 items, default [] |
data.brief.details.preferredFeatures | string[] | at most 50 items, default [] |
data.brief.details.excludedFeatures | string[] | at most 50 items, default [] |
data.brief.details.imageDescription | string | at most 2000 characters, default "" |
data.brief.details.imageTags | string[] | at most 50 items, default [] |
data.brief.details.radius | object, optional | |
data.brief.details.radius.latitude | number | -90 to 90 |
data.brief.details.radius.longitude | number | -180 to 180 |
data.brief.details.radius.km | number | at most 2000 |
data.brief.details.area | object | default {} |
data.brief.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.brief.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.brief.likedHomes | object | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
data.brief.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.brief.likedHomes.importance | "nice" | "must" | default "nice" |
data.brief.feel | object | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
data.brief.feel.text | string | at most 2000 characters, default "" |
data.brief.feel.tags | string[] | at most 12 items, default [] |
data.brief.feel.importance | "nice" | "must" | default "nice" |
data.brief.inferred | object | null, optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
data.brief.inferred.fields | string[] | at most 40 items |
data.brief.inferred.assumed | string[] | at most 10 items |
data.search | object | The search the words describe, in the shape POST /api/v1/searches takes. inferred says what was read and what was assumed. |
data.search.title | string | What you call this brief: 1 to 120 characters.1 to 120 characters |
data.search.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
data.search.source | string | null, optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
data.search.tenure | "buy" | "rent" | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
data.search.budget | object | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
data.search.budget.value | number | at most 100000000 |
data.search.budget.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
data.search.budget.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
data.search.beds | object | The fewest bedrooms. Firm unless you say otherwise. |
data.search.beds.value | integer | 0 to 30 |
data.search.beds.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.search.beds.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.search.baths | object | The fewest bathrooms. Firm unless you say otherwise. |
data.search.baths.value | number | 0 to 30 |
data.search.baths.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.search.baths.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.search.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | default "Any" |
data.search.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | default "Exploring" |
data.search.areas | string[] | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
data.search.commutes | object[] | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
data.search.commutes[].id | string, optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
data.search.commutes[].label | string | 1 to 200 characters |
data.search.commutes[].coords | object | null | Where the stop is. Null when kind is any: the match finds the places. |
data.search.commutes[].coords.lat | number | -90 to 90 |
data.search.commutes[].coords.lng | number | -180 to 180 |
data.search.commutes[].maxMin | integer | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
data.search.commutes[].kind | "place" | "any", optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
data.search.commutes[].query | string, optional | The brand, when kind is any.1 to 80 characters |
data.search.areaPins | object[] | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
data.search.areaPins[].id | string, optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
data.search.areaPins[].lat | number | -90 to 90 |
data.search.areaPins[].lng | number | -180 to 180 |
data.search.mustHaves | string[] | at most 20 items, default [] |
data.search.niceToHaves | string[] | at most 20 items, default [] |
data.search.details | object | null, optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
data.search.details.text | string | at most 4000 characters, default "" |
data.search.details.textMode | "prefer" | "require" | default "prefer" |
data.search.details.numeric | object | default {} |
data.search.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.search.details.cities | string[] | at most 100 items, default [] |
data.search.details.postalCodes | string[] | at most 100 items, default [] |
data.search.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.search.details.requiredFeatures | string[] | at most 50 items, default [] |
data.search.details.preferredFeatures | string[] | at most 50 items, default [] |
data.search.details.excludedFeatures | string[] | at most 50 items, default [] |
data.search.details.imageDescription | string | at most 2000 characters, default "" |
data.search.details.imageTags | string[] | at most 50 items, default [] |
data.search.details.radius | object, optional | |
data.search.details.radius.latitude | number | -90 to 90 |
data.search.details.radius.longitude | number | -180 to 180 |
data.search.details.radius.km | number | at most 2000 |
data.search.details.area | object | default {} |
data.search.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.search.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.search.likedHomes | object | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
data.search.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.search.likedHomes.importance | "nice" | "must" | default "nice" |
data.search.feel | object | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
data.search.feel.text | string | at most 2000 characters, default "" |
data.search.feel.tags | string[] | at most 12 items, default [] |
data.search.feel.importance | "nice" | "must" | default "nice" |
data.search.inferred | object | null, optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
data.search.inferred.fields | string[] | at most 40 items |
data.search.inferred.assumed | string[] | at most 10 items |
data.setAside | string[] | The kinds of phrase Fair Housing rules kept out of the draft, by class and never by their words. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | draft-empty | The words name nothing a search can use. |
| 429 | rate-limited | The hour's draft cap is spent; Retry-After says when it lifts. |
| 502 | ai-failed | The model failed or answered out of shape. Nothing was drafted. |
| 503 | ai-unavailable | No model is configured on this deployment. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 monthly-cap-reached500 internal
Read a search
/api/v1/searches/{searchId}One of your searches at its latest revision. The ETag is the revision; send it back as If-Match to change the search without overwriting a change you have not seen.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
searchId | Path | string (uuid) | Required | The search's id. |
Examples
curl "https://liviamatch.com/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
The search.
HeadersETag
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches",
"runs": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/runs"
}
}
}Fields (73)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | The old path of runs, which still answers. |
data.links.runs | string | Where to POST to run the search ("Search now"), and GET to list its runs. |
Errors
| Status | Code | When |
|---|---|---|
| 404 | not-found | No such search in your workspace. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Save a search at an id
/api/v1/searches/{searchId}Saves a search at an id you choose, which makes a retry land once: 201 when the id is new, 200 when it names one of your searches, and 409 when it belongs to someone else's, which is never touched. A change is saved as a new revision, as the app's editor saves it, and the matches already made keep the revision they ran. Saving what the latest revision already says writes nothing and answers 200.
Send If-Match with the ETag you last read to be sure you are not overwriting someone else's change; a stale version is 412 and nothing is written. What you leave out takes its default, so send the whole search: a source you leave out keeps the search's own.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
searchId | Path | string (uuid) | Required | The search's id. |
If-Match | Header | string | Optional | The ETag of the revision you read, such as "2". If the search has changed since, nothing is written and the answer is 412. |
Request body
The whole search, as for making one.
Fields (64)
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | What you call this brief: 1 to 120 characters.1 to 120 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
source | string | null | Optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
tenure | "buy" | "rent" | Optional | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
budget | object | Required | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
budget.value | number | Required | at most 100000000 |
budget.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
budget.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
beds | object | Required | The fewest bedrooms. Firm unless you say otherwise. |
beds.value | integer | Required | 0 to 30 |
beds.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
beds.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
baths | object | Required | The fewest bathrooms. Firm unless you say otherwise. |
baths.value | number | Required | 0 to 30 |
baths.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
baths.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | Optional | default "Any" |
timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | Optional | default "Exploring" |
areas | string[] | Optional | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
commutes | object[] | Optional | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
commutes[].id | string | Optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
commutes[].label | string | Required | 1 to 200 characters |
commutes[].coords | object | null | Required | Where the stop is. Null when kind is any: the match finds the places. |
commutes[].coords.lat | number | Required | -90 to 90 |
commutes[].coords.lng | number | Required | -180 to 180 |
commutes[].maxMin | integer | Required | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
commutes[].kind | "place" | "any" | Optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
commutes[].query | string | Optional | The brand, when kind is any.1 to 80 characters |
areaPins | object[] | Optional | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
areaPins[].id | string | Optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
areaPins[].lat | number | Required | -90 to 90 |
areaPins[].lng | number | Required | -180 to 180 |
mustHaves | string[] | Optional | at most 20 items, default [] |
niceToHaves | string[] | Optional | at most 20 items, default [] |
details | object | null | Optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
details.text | string | Optional | at most 4000 characters, default "" |
details.textMode | "prefer" | "require" | Optional | default "prefer" |
details.numeric | object | Optional | default {} |
details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | Optional | at most 7 items, default [] |
details.cities | string[] | Optional | at most 100 items, default [] |
details.postalCodes | string[] | Optional | at most 100 items, default [] |
details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | Optional | at least 1 items, default ["Active"] |
details.requiredFeatures | string[] | Optional | at most 50 items, default [] |
details.preferredFeatures | string[] | Optional | at most 50 items, default [] |
details.excludedFeatures | string[] | Optional | at most 50 items, default [] |
details.imageDescription | string | Optional | at most 2000 characters, default "" |
details.imageTags | string[] | Optional | at most 50 items, default [] |
details.radius | object | Optional | |
details.radius.latitude | number | Required | -90 to 90 |
details.radius.longitude | number | Required | -180 to 180 |
details.radius.km | number | Required | at most 2000 |
details.area | object | Optional | default {} |
details.maxEvidenceAgeDays | integer | Optional | at most 3650, default 365 |
details.missingData | "exclude-required" | "penalize" | Optional | default "exclude-required" |
likedHomes | object | Optional | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
likedHomes.urls | string (uri)[] | Optional | at most 4 items, default [] |
likedHomes.importance | "nice" | "must" | Optional | default "nice" |
feel | object | Optional | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
feel.text | string | Optional | at most 2000 characters, default "" |
feel.tags | string[] | Optional | at most 12 items, default [] |
feel.importance | "nice" | "must" | Optional | default "nice" |
inferred | object | null | Optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
inferred.fields | string[] | Required | at most 40 items |
inferred.assumed | string[] | Required | at most 10 items |
{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 700000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}Examples
curl -X PUT "https://liviamatch.com/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 700000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}'Responses
A new search was saved at revision 1.
HeadersETagLocation
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches",
"runs": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/runs"
}
}
}Fields (73)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | The old path of runs, which still answers. |
data.links.runs | string | Where to POST to run the search ("Search now"), and GET to list its runs. |
An existing search was saved as a new revision, or already said this.
HeadersETag
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 2,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 700000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T14:40:09.771Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches",
"runs": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/runs"
}
}
}Fields (73)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | The old path of runs, which still answers. |
data.links.runs | string | Where to POST to run the search ("Search now"), and GET to list its runs. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 400 | invalid-if-match | The If-Match is not a revision. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | source-unavailable | source is not Homes for sale and not an active data set of yours (GET /api/v1/data-sets). |
| 409 | id-unavailable | The id belongs to a search that is not yours. |
| 412 | revision-mismatch | The search changed since the revision in If-Match. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
List searches
/api/v1/briefsYour searches, each at its latest revision, newest saved first. Archived searches stay out unless you ask. Pages by cursor, 20 at a time and up to 50.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
status | Query | "active" | "archived" | "all" | Optional | active (the default), archived or all. It stays on the next page's link. |
limit | Query | integer | Optional | How many to return: 1 to 50, 20 when absent. A number outside that range is clamped; text is ignored. |
cursor | Query | string | Optional | Where the last page ended: the cursor inside the previous page's links.next. Pass links as given; a cursor you edit is refused. |
Examples
curl "https://liviamatch.com/api/v1/briefs" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
One page of searches.
{
"data": [
{
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches"
}
}
],
"links": {
"next": null
}
}Fields (74)
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].id | string (uuid) | |
data[].version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data[].title | string | |
data[].clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data[].source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data[].tenure | "buy" | "rent" | |
data[].budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data[].budget.value | number | The target. |
data[].budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].beds | object | The fewest bedrooms. |
data[].beds.value | number | The target. |
data[].beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].baths | object | The fewest bathrooms. |
data[].baths.value | number | The target. |
data[].baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data[].baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data[].propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data[].timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data[].areas | string[] | The cities to look in. |
data[].commutes | object[] | |
data[].commutes[].id | string | 1 to 64 characters |
data[].commutes[].label | string | at most 200 characters |
data[].commutes[].coords | object | null | |
data[].commutes[].coords.lat | number | |
data[].commutes[].coords.lng | number | |
data[].commutes[].maxMin | integer | 0 to 60 |
data[].commutes[].kind | "place" | "any", optional | |
data[].commutes[].query | string, optional | 1 to 80 characters |
data[].areaPins | object[] | |
data[].areaPins[].id | string | 1 to 64 characters |
data[].areaPins[].lat | number | |
data[].areaPins[].lng | number | |
data[].mustHaves | string[] | |
data[].niceToHaves | string[] | |
data[].details | object | null | Advanced criteria, or null when none were set. |
data[].details.text | string | at most 4000 characters, default "" |
data[].details.textMode | "prefer" | "require" | default "prefer" |
data[].details.numeric | object | default {} |
data[].details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data[].details.cities | string[] | at most 100 items, default [] |
data[].details.postalCodes | string[] | at most 100 items, default [] |
data[].details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data[].details.requiredFeatures | string[] | at most 50 items, default [] |
data[].details.preferredFeatures | string[] | at most 50 items, default [] |
data[].details.excludedFeatures | string[] | at most 50 items, default [] |
data[].details.imageDescription | string | at most 2000 characters, default "" |
data[].details.imageTags | string[] | at most 50 items, default [] |
data[].details.radius | object, optional | |
data[].details.radius.latitude | number | -90 to 90 |
data[].details.radius.longitude | number | -180 to 180 |
data[].details.radius.km | number | at most 2000 |
data[].details.area | object | default {} |
data[].details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data[].details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data[].likedHomes | object | |
data[].likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data[].likedHomes.importance | "nice" | "must" | default "nice" |
data[].feel | object | |
data[].feel.text | string | at most 2000 characters, default "" |
data[].feel.tags | string[] | at most 12 items, default [] |
data[].feel.importance | "nice" | "must" | default "nice" |
data[].inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data[].inferred.fields | string[] | at most 40 items |
data[].inferred.assumed | string[] | at most 10 items |
data[].archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data[].updatedAt | string (date-time) | When this revision was saved. |
data[].links | object | |
data[].links.self | string | |
data[].links.matches | string | Where to POST to start a match, and GET to list this brief's matches. |
links | object | |
links.next | string | null | The URL of the next page, or null when this is the last. Follow it as given. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The status is not active, archived or all. |
| 400 | invalid-cursor | The cursor is not one a page linked to. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Make a search
/api/v1/briefsMakes a search from structure and answers 201 with it, its ETag and a Location. The server picks the id.
Budget, beds and baths are required, so a search is never made of starting values. Everything else has an honest default: no preference, an empty list. To start from words, draft one and send what it returned.
A POST that times out may or may not have landed. To make a retry safe, choose the id yourself and use PUT /api/v1/searches/{searchId}: the same id with the same words lands once.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Request body
The whole search. Unknown fields are refused, so a typo fails loudly.
Fields (64)
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | What you call this brief: 1 to 120 characters.1 to 120 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
source | string | null | Optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
tenure | "buy" | "rent" | Optional | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
budget | object | Required | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
budget.value | number | Required | at most 100000000 |
budget.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
budget.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
beds | object | Required | The fewest bedrooms. Firm unless you say otherwise. |
beds.value | integer | Required | 0 to 30 |
beds.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
beds.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
baths | object | Required | The fewest bathrooms. Firm unless you say otherwise. |
baths.value | number | Required | 0 to 30 |
baths.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
baths.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | Optional | default "Any" |
timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | Optional | default "Exploring" |
areas | string[] | Optional | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
commutes | object[] | Optional | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
commutes[].id | string | Optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
commutes[].label | string | Required | 1 to 200 characters |
commutes[].coords | object | null | Required | Where the stop is. Null when kind is any: the match finds the places. |
commutes[].coords.lat | number | Required | -90 to 90 |
commutes[].coords.lng | number | Required | -180 to 180 |
commutes[].maxMin | integer | Required | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
commutes[].kind | "place" | "any" | Optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
commutes[].query | string | Optional | The brand, when kind is any.1 to 80 characters |
areaPins | object[] | Optional | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
areaPins[].id | string | Optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
areaPins[].lat | number | Required | -90 to 90 |
areaPins[].lng | number | Required | -180 to 180 |
mustHaves | string[] | Optional | at most 20 items, default [] |
niceToHaves | string[] | Optional | at most 20 items, default [] |
details | object | null | Optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
details.text | string | Optional | at most 4000 characters, default "" |
details.textMode | "prefer" | "require" | Optional | default "prefer" |
details.numeric | object | Optional | default {} |
details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | Optional | at most 7 items, default [] |
details.cities | string[] | Optional | at most 100 items, default [] |
details.postalCodes | string[] | Optional | at most 100 items, default [] |
details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | Optional | at least 1 items, default ["Active"] |
details.requiredFeatures | string[] | Optional | at most 50 items, default [] |
details.preferredFeatures | string[] | Optional | at most 50 items, default [] |
details.excludedFeatures | string[] | Optional | at most 50 items, default [] |
details.imageDescription | string | Optional | at most 2000 characters, default "" |
details.imageTags | string[] | Optional | at most 50 items, default [] |
details.radius | object | Optional | |
details.radius.latitude | number | Required | -90 to 90 |
details.radius.longitude | number | Required | -180 to 180 |
details.radius.km | number | Required | at most 2000 |
details.area | object | Optional | default {} |
details.maxEvidenceAgeDays | integer | Optional | at most 3650, default 365 |
details.missingData | "exclude-required" | "penalize" | Optional | default "exclude-required" |
likedHomes | object | Optional | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
likedHomes.urls | string (uri)[] | Optional | at most 4 items, default [] |
likedHomes.importance | "nice" | "must" | Optional | default "nice" |
feel | object | Optional | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
feel.text | string | Optional | at most 2000 characters, default "" |
feel.tags | string[] | Optional | at most 12 items, default [] |
feel.importance | "nice" | "must" | Optional | default "nice" |
inferred | object | null | Optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
inferred.fields | string[] | Required | at most 40 items |
inferred.assumed | string[] | Required | at most 10 items |
{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 750000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}Examples
curl -X POST "https://liviamatch.com/api/v1/briefs" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 750000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}'Responses
The search was made, at revision 1.
HeadersETagLocation
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches"
}
}
}Fields (72)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | Where to POST to start a match, and GET to list this brief's matches. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | source-unavailable | source is not Homes for sale and not an active data set of yours (GET /api/v1/data-sets). |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Draft a search from words
/api/v1/briefs/draftReads notes about a client (a consultation, an email, a dictation) and answers the search they describe, in the shape POST /api/v1/searches takes. It saves nothing: look at it, change what is wrong, then send it.
search.inferred (brief.inferred on the old path) names what the words said (fields) and the essentials they never mentioned (assumed). Those are budget and propertyType, each left at the value a new search starts with. A search drafted before 2026-10-22 may also list beds and baths. Beds and baths the words never named are now drafted with no floor (value 0). A field that claims nothing, like that or timeline at Exploring, is not an assumption and is never listed. Pass inferred back when you save, so the assumptions stay named on the search, and set the assumed essentials yourself when you know them.
The words go to the model under the no-retention routing rule, pass the Fair Housing scrub before and after, and are never stored or logged. A draft counts against an hourly cap, and words that name nothing a search can use are refused rather than drafted from starting values.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Request body
The notes to read, and the client they are about when there is one.
Fields (2)
| Field | Type | Required | Description |
|---|---|---|---|
text | string | Required | What the agent heard or wrote about the client: a consultation, an email, notes. Up to 10,000 characters. It goes to the model under the no-retention routing rule and is never stored.1 to 10000 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null. It rides on the draft so it can be saved as it is.default null |
{
"text": "Amara and her husband are moving from Boston in January with two kids and a dog. Budget is around $750k, three bedrooms at least, two baths, somewhere in Nashville near Vanderbilt hospital. A fenced yard is a must, and a home office would be nice.",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28"
}Examples
curl -X POST "https://liviamatch.com/api/v1/briefs/draft" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Amara and her husband are moving from Boston in January with two kids and a dog. Budget is around $750k, three bedrooms at least, two baths, somewhere in Nashville near Vanderbilt hospital. A fenced yard is a must, and a home office would be nice.",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28"
}'Responses
A draft, ready to look at and save.
{
"data": {
"brief": {
"title": "Boston family, near Vanderbilt",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [],
"areaPins": [],
"mustHaves": [
"Fenced yard"
],
"niceToHaves": [
"Home office"
],
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": {
"fields": [
"title",
"budget",
"beds",
"baths",
"cities",
"feature"
],
"assumed": [
"propertyType"
]
}
},
"setAside": []
}
}Fields (67)
| Field | Type | Description |
|---|---|---|
data | object | |
data.brief | object | The search the words describe, in the shape POST /api/v1/searches takes. inferred says what was read and what was assumed. |
data.brief.title | string | What you call this brief: 1 to 120 characters.1 to 120 characters |
data.brief.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
data.brief.source | string | null, optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
data.brief.tenure | "buy" | "rent" | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
data.brief.budget | object | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
data.brief.budget.value | number | at most 100000000 |
data.brief.budget.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
data.brief.budget.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
data.brief.beds | object | The fewest bedrooms. Firm unless you say otherwise. |
data.brief.beds.value | integer | 0 to 30 |
data.brief.beds.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.brief.beds.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.brief.baths | object | The fewest bathrooms. Firm unless you say otherwise. |
data.brief.baths.value | number | 0 to 30 |
data.brief.baths.fixed | boolean | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
data.brief.baths.buffer | number | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
data.brief.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | default "Any" |
data.brief.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | default "Exploring" |
data.brief.areas | string[] | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
data.brief.commutes | object[] | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
data.brief.commutes[].id | string, optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
data.brief.commutes[].label | string | 1 to 200 characters |
data.brief.commutes[].coords | object | null | Where the stop is. Null when kind is any: the match finds the places. |
data.brief.commutes[].coords.lat | number | -90 to 90 |
data.brief.commutes[].coords.lng | number | -180 to 180 |
data.brief.commutes[].maxMin | integer | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
data.brief.commutes[].kind | "place" | "any", optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
data.brief.commutes[].query | string, optional | The brand, when kind is any.1 to 80 characters |
data.brief.areaPins | object[] | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
data.brief.areaPins[].id | string, optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
data.brief.areaPins[].lat | number | -90 to 90 |
data.brief.areaPins[].lng | number | -180 to 180 |
data.brief.mustHaves | string[] | at most 20 items, default [] |
data.brief.niceToHaves | string[] | at most 20 items, default [] |
data.brief.details | object | null, optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
data.brief.details.text | string | at most 4000 characters, default "" |
data.brief.details.textMode | "prefer" | "require" | default "prefer" |
data.brief.details.numeric | object | default {} |
data.brief.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.brief.details.cities | string[] | at most 100 items, default [] |
data.brief.details.postalCodes | string[] | at most 100 items, default [] |
data.brief.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.brief.details.requiredFeatures | string[] | at most 50 items, default [] |
data.brief.details.preferredFeatures | string[] | at most 50 items, default [] |
data.brief.details.excludedFeatures | string[] | at most 50 items, default [] |
data.brief.details.imageDescription | string | at most 2000 characters, default "" |
data.brief.details.imageTags | string[] | at most 50 items, default [] |
data.brief.details.radius | object, optional | |
data.brief.details.radius.latitude | number | -90 to 90 |
data.brief.details.radius.longitude | number | -180 to 180 |
data.brief.details.radius.km | number | at most 2000 |
data.brief.details.area | object | default {} |
data.brief.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.brief.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.brief.likedHomes | object | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
data.brief.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.brief.likedHomes.importance | "nice" | "must" | default "nice" |
data.brief.feel | object | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
data.brief.feel.text | string | at most 2000 characters, default "" |
data.brief.feel.tags | string[] | at most 12 items, default [] |
data.brief.feel.importance | "nice" | "must" | default "nice" |
data.brief.inferred | object | null, optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
data.brief.inferred.fields | string[] | at most 40 items |
data.brief.inferred.assumed | string[] | at most 10 items |
data.setAside | string[] | The kinds of phrase Fair Housing rules kept out of the draft, by class and never by their words. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | draft-empty | The words name nothing a search can use. |
| 429 | rate-limited | The hour's draft cap is spent; Retry-After says when it lifts. |
| 502 | ai-failed | The model failed or answered out of shape. Nothing was drafted. |
| 503 | ai-unavailable | No model is configured on this deployment. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 monthly-cap-reached500 internal
Read a search
/api/v1/briefs/{searchId}One of your searches at its latest revision. The ETag is the revision; send it back as If-Match to change the search without overwriting a change you have not seen.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:read- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
searchId | Path | string (uuid) | Required | The search's id. |
Examples
curl "https://liviamatch.com/api/v1/briefs/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592" \
-H "Authorization: Bearer $LIVIA_API_KEY"Responses
The search.
HeadersETag
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches"
}
}
}Fields (72)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | Where to POST to start a match, and GET to list this brief's matches. |
Errors
| Status | Code | When |
|---|---|---|
| 404 | not-found | No such search in your workspace. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal
Save a search at an id
/api/v1/briefs/{searchId}Saves a search at an id you choose, which makes a retry land once: 201 when the id is new, 200 when it names one of your searches, and 409 when it belongs to someone else's, which is never touched. A change is saved as a new revision, as the app's editor saves it, and the matches already made keep the revision they ran. Saving what the latest revision already says writes nothing and answers 200.
Send If-Match with the ETag you last read to be sure you are not overwriting someone else's change; a stale version is 412 and nothing is written. What you leave out takes its default, so send the whole search: a source you leave out keeps the search's own.
An API key carrying the scope named here, or a signed-in session.
- Scope
briefs:write- Key kinds
- PersonalOrganization
Required and optional fields, allowed values and errors, as the page shows them.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
searchId | Path | string (uuid) | Required | The search's id. |
If-Match | Header | string | Optional | The ETag of the revision you read, such as "2". If the search has changed since, nothing is written and the answer is 412. |
Request body
The whole search, as for making one.
Fields (64)
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Required | What you call this brief: 1 to 120 characters.1 to 120 characters |
clientId | string (uuid) | null | Optional | The client it is for, or null for a brief that is nobody's yet. Attach a client later by saving the brief again.default null |
source | string | null | Optional | What to match against: homes-for-sale, or the source of one of your data sets (GET /api/v1/data-sets). Left out, it starts on the same source a new brief in the app does. Null means none yet, and the brief cannot run.1 to 100 characters |
tenure | "buy" | "rent" | Optional | Whether the client is buying or renting. For rent, budget is a monthly rent.default "buy" |
budget | object | Required | The most the client will pay: a price, or a monthly rent. Flexible by 10 percent unless you say otherwise. |
budget.value | number | Required | at most 100000000 |
budget.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default false |
budget.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 10 |
beds | object | Required | The fewest bedrooms. Firm unless you say otherwise. |
beds.value | integer | Required | 0 to 30 |
beds.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
beds.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
baths | object | Required | The fewest bathrooms. Firm unless you say otherwise. |
baths.value | number | Required | 0 to 30 |
baths.fixed | boolean | Optional | true holds the value exactly. false lets a home miss it by up to buffer percent and still count.default true |
baths.buffer | number | Optional | How many percent a home may miss the value by when fixed is false. Ignored when it is true.0 to 50, default 0 |
propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | Optional | default "Any" |
timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | Optional | default "Exploring" |
areas | string[] | Optional | The cities to look in. With none, Homes for sale looks around the organization's market, else Nashville, TN.at most 20 items, default [] |
commutes | object[] | Optional | Up to three places the client travels to. A data set cannot time travel, so a search with a commute cannot run against one.at most 3 items, default [] |
commutes[].id | string | Optional | Your own id for this stop, or leave it out and one is made.1 to 64 characters |
commutes[].label | string | Required | 1 to 200 characters |
commutes[].coords | object | null | Required | Where the stop is. Null when kind is any: the match finds the places. |
commutes[].coords.lat | number | Required | -90 to 90 |
commutes[].coords.lng | number | Required | -180 to 180 |
commutes[].maxMin | integer | Required | The longest drive the client will take, in minutes. 0 means no ceiling.0 to 60 |
commutes[].kind | "place" | "any" | Optional | any is a brand, such as Starbucks, rather than one address. Leave it out for one place. |
commutes[].query | string | Optional | The brand, when kind is any.1 to 80 characters |
areaPins | object[] | Optional | Pins on the map that widen where to look. Not drive ceilings.at most 4 items, default [] |
areaPins[].id | string | Optional | Your own id for this pin, or leave it out and one is made.1 to 64 characters |
areaPins[].lat | number | Required | -90 to 90 |
areaPins[].lng | number | Required | -180 to 180 |
mustHaves | string[] | Optional | at most 20 items, default [] |
niceToHaves | string[] | Optional | at most 20 items, default [] |
details | object | null | Optional | Advanced criteria: ranges (square feet, lot, HOA, days on market, and more), features to avoid, keywords, each with its importance. What POST /searches/draft writes when the words name one. |
details.text | string | Optional | at most 4000 characters, default "" |
details.textMode | "prefer" | "require" | Optional | default "prefer" |
details.numeric | object | Optional | default {} |
details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | Optional | at most 7 items, default [] |
details.cities | string[] | Optional | at most 100 items, default [] |
details.postalCodes | string[] | Optional | at most 100 items, default [] |
details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | Optional | at least 1 items, default ["Active"] |
details.requiredFeatures | string[] | Optional | at most 50 items, default [] |
details.preferredFeatures | string[] | Optional | at most 50 items, default [] |
details.excludedFeatures | string[] | Optional | at most 50 items, default [] |
details.imageDescription | string | Optional | at most 2000 characters, default "" |
details.imageTags | string[] | Optional | at most 50 items, default [] |
details.radius | object | Optional | |
details.radius.latitude | number | Required | -90 to 90 |
details.radius.longitude | number | Required | -180 to 180 |
details.radius.km | number | Required | at most 2000 |
details.area | object | Optional | default {} |
details.maxEvidenceAgeDays | integer | Optional | at most 3650, default 365 |
details.missingData | "exclude-required" | "penalize" | Optional | default "exclude-required" |
likedHomes | object | Optional | Links to homes the client liked, and how much they matter.default {"urls":[],"importance":"nice"} |
likedHomes.urls | string (uri)[] | Optional | at most 4 items, default [] |
likedHomes.importance | "nice" | "must" | Optional | default "nice" |
feel | object | Optional | How the home should feel, in words and tags, and how much it matters.default {"text":"","tags":[],"importance":"nice"} |
feel.text | string | Optional | at most 2000 characters, default "" |
feel.tags | string[] | Optional | at most 12 items, default [] |
feel.importance | "nice" | "must" | Optional | default "nice" |
inferred | object | null | Optional | What was read from words and what was assumed. Pass back what POST /searches/draft returned so the assumptions stay named. |
inferred.fields | string[] | Required | at most 40 items |
inferred.assumed | string[] | Required | at most 10 items |
{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 700000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}Examples
curl -X PUT "https://liviamatch.com/api/v1/briefs/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592" \
-H "Authorization: Bearer $LIVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"budget": {
"value": 700000
},
"beds": {
"value": 3
},
"baths": {
"value": 2
},
"propertyType": "House",
"areas": [
"Nashville"
],
"commutes": [
{
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
]
}'Responses
A new search was saved at revision 1.
HeadersETagLocation
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 1,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 750000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T13:58:20.310Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches"
}
}
}Fields (72)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | Where to POST to start a match, and GET to list this brief's matches. |
An existing search was saved as a new revision, or already said this.
HeadersETag
{
"data": {
"id": "7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"version": 2,
"title": "Okafors, East Nashville",
"clientId": "9c4e1a73-2b85-4d06-8f1a-7d3b5e9c0a28",
"source": "homes-for-sale",
"tenure": "buy",
"budget": {
"value": 700000,
"fixed": false,
"buffer": 10
},
"beds": {
"value": 3,
"fixed": true,
"buffer": 0
},
"baths": {
"value": 2,
"fixed": true,
"buffer": 0
},
"propertyType": "House",
"timeline": "Exploring",
"areas": [
"Nashville"
],
"commutes": [
{
"id": "commute-1",
"label": "Vanderbilt University Medical Center",
"coords": {
"lat": 36.1421,
"lng": -86.8016
},
"maxMin": 25
}
],
"areaPins": [],
"mustHaves": [
"Fenced yard",
"Walkable to coffee"
],
"niceToHaves": [
"Home office"
],
"details": null,
"likedHomes": {
"urls": [],
"importance": "nice"
},
"feel": {
"text": "",
"tags": [],
"importance": "nice"
},
"inferred": null,
"archivedAt": null,
"updatedAt": "2026-10-06T14:40:09.771Z",
"links": {
"self": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592",
"matches": "/api/v1/searches/7d2f8a40-b6c1-4e35-9a07-c4e1d8b3f592/matches"
}
}
}Fields (72)
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string (uuid) | |
data.version | integer | The revision. It counts up on every save; the same number is the ETag.at least 1 |
data.title | string | |
data.clientId | string (uuid) | null | The client it is for, or null for a brief that is nobody's yet. |
data.source | string | null | What it matches against: homes-for-sale, a data set's source, or null when none is chosen yet (the brief cannot run). |
data.tenure | "buy" | "rent" | |
data.budget | object | The most the client will pay: a price, or a monthly rent for rent. |
data.budget.value | number | The target. |
data.budget.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.budget.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.beds | object | The fewest bedrooms. |
data.beds.value | number | The target. |
data.beds.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.beds.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.baths | object | The fewest bathrooms. |
data.baths.value | number | The target. |
data.baths.fixed | boolean | true holds the value exactly; false lets a home miss it by up to buffer percent. |
data.baths.buffer | number | How many percent a home may miss the value by when fixed is false. |
data.propertyType | "Any" | "House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other" | |
data.timeline | "Exploring" | "Within 3 months" | "3–6 months" | "6+ months" | |
data.areas | string[] | The cities to look in. |
data.commutes | object[] | |
data.commutes[].id | string | 1 to 64 characters |
data.commutes[].label | string | at most 200 characters |
data.commutes[].coords | object | null | |
data.commutes[].coords.lat | number | |
data.commutes[].coords.lng | number | |
data.commutes[].maxMin | integer | 0 to 60 |
data.commutes[].kind | "place" | "any", optional | |
data.commutes[].query | string, optional | 1 to 80 characters |
data.areaPins | object[] | |
data.areaPins[].id | string | 1 to 64 characters |
data.areaPins[].lat | number | |
data.areaPins[].lng | number | |
data.mustHaves | string[] | |
data.niceToHaves | string[] | |
data.details | object | null | Advanced criteria, or null when none were set. |
data.details.text | string | at most 4000 characters, default "" |
data.details.textMode | "prefer" | "require" | default "prefer" |
data.details.numeric | object | default {} |
data.details.propertyTypes | ("House" | "Townhouse" | "Condo" | "Multi-family" | "Land" | "Manufactured" | "Other")[] | at most 7 items, default [] |
data.details.cities | string[] | at most 100 items, default [] |
data.details.postalCodes | string[] | at most 100 items, default [] |
data.details.statuses | ("Active" | "Pending" | "Sold" | "Withdrawn")[] | at least 1 items, default ["Active"] |
data.details.requiredFeatures | string[] | at most 50 items, default [] |
data.details.preferredFeatures | string[] | at most 50 items, default [] |
data.details.excludedFeatures | string[] | at most 50 items, default [] |
data.details.imageDescription | string | at most 2000 characters, default "" |
data.details.imageTags | string[] | at most 50 items, default [] |
data.details.radius | object, optional | |
data.details.radius.latitude | number | -90 to 90 |
data.details.radius.longitude | number | -180 to 180 |
data.details.radius.km | number | at most 2000 |
data.details.area | object | default {} |
data.details.maxEvidenceAgeDays | integer | at most 3650, default 365 |
data.details.missingData | "exclude-required" | "penalize" | default "exclude-required" |
data.likedHomes | object | |
data.likedHomes.urls | string (uri)[] | at most 4 items, default [] |
data.likedHomes.importance | "nice" | "must" | default "nice" |
data.feel | object | |
data.feel.text | string | at most 2000 characters, default "" |
data.feel.tags | string[] | at most 12 items, default [] |
data.feel.importance | "nice" | "must" | default "nice" |
data.inferred | object | null | What was read from words and what was assumed, when the brief came from a draft; null otherwise. |
data.inferred.fields | string[] | at most 40 items |
data.inferred.assumed | string[] | at most 10 items |
data.archivedAt | string (date-time) | null | When it was archived in the app, or null. |
data.updatedAt | string (date-time) | When this revision was saved. |
data.links | object | |
data.links.self | string | |
data.links.matches | string | Where to POST to start a match, and GET to list this brief's matches. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid | The body failed validation; issues names each field. |
| 400 | invalid-json | The body is empty or not JSON. |
| 413 | too-large | The body is over 64 KiB. |
| 400 | invalid-if-match | The If-Match is not a revision. |
| 422 | unknown-client | clientId is not a client in your workspace. |
| 422 | source-unavailable | source is not Homes for sale and not an active data set of yours (GET /api/v1/data-sets). |
| 409 | id-unavailable | The id belongs to a search that is not yours. |
| 412 | revision-mismatch | The search changed since the revision in If-Match. |
Any call of this kind can also answer401 unauthenticated401 invalid-api-key403 insufficient-scope429 rate-limited429 monthly-cap-reached500 internal