Search or Ask
Search or ask lets one search field answer questions too: a judge reads what is typed on each pause and offers to answer it, and an answer is written, on request, from the dataset's own records. The Search or Ask guide explains the settings and the front end; this page is the API.
The routes answer 404 until the instance shows Search or ask (the console's Admin > Settings > Experimental) and a team admin shows it on the dataset (its Options tab). They answer 409 searchOrAskDisabled while the dataset's settings have it switched off, and 409 searchOrAskUnavailable where the instance has no model key.
All routes are under /api/teams/{teamName}/datasets/{dataSetName}/search-or-ask. A Search key is enough for everything a visitor's page calls: the settings, intent, filters and ask.
Judge what is typed
Call intent on a pause in typing, beside the search the page already runs.
To have the judgement ready when the pause comes, also call it while the visitor types, with "prefetch": true and one call in flight at a time: when one returns, send the next for whatever the field holds by then. The judge reads the text ahead and keeps its answer, so the pause's own call comes back at once. Only the pause's call counts as a judgement in the statistics, and calls made ahead never use the last fifth of the day's budget, which stays for the pauses. useSearchOrAsk() does this for you.
POST /api/teams/{teamName}/datasets/{dataSetName}/search-or-ask/intent?session={pageLoadId}&queryId={searchQueryId}
Content-Type: application/json
{ "text": "how do i set up boost rules" }{
"p": 0.91,
"answerable": 0.84,
"criteria": null,
"offer": "strong",
"thresholds": { "strong": 0.65, "strongHide": 0.45, "invite": 0.4, "inviteHide": 0.3 },
"cached": false,
"ms": 312,
"searchText": "set up boost rules",
"refocusWhileTyping": true,
"inviteOnAskPhrase": true,
"cutWhileAsking": true,
"cutShareOfTop": 0.5,
"cutTopFloor": 0.2,
"strictCoverageWhileAsking": true,
"filtersWhileTyping": "off",
"mode": "soft"
}| Field | Meaning |
|---|---|
p | The probability the visitor is asking something an answer would serve, 0 to 1 |
answerable | Whether the results the judge read could answer it |
criteria | On a catalogue, the probability the text describes what to list; null on documentation |
offer | none, invite or strong, from p (or criteria) and the thresholds |
searchText | What the list searches for this text, its Ask phrases taken off |
finished | Whether the visitor has finished typing it, 0 to 1: what type is has not. Proactive answers on its own only from thresholds.finished |
mode | The dataset's mode. proactive comes back as inviting once the day's proactive answers are spent, so a page stops starting answers on its own |
reason | Set when the judge did not answer: timeout, failed:{status}, or identifier for a code the judge is never asked about. p is then 0; the call itself never fails |
The other fields are the dataset's While asking settings, sent with every judgement so a change applies on the next pause. A front end keeps an offer up while p stays above the …Hide threshold, so it does not blink as the visitor types on. Text is 1 to 300 characters.
Answer a question
POST /api/teams/{teamName}/datasets/{dataSetName}/search-or-ask/ask?session={pageLoadId}
Content-Type: application/json
{ "question": "how do i set up boost rules", "history": [] }The answer streams as server-sent events:
event: status
data: {"step":"Searching"}
event: sources
data: [{"n":1,"documentKey":42,"title":"Boost Rules"},{"n":2,"documentKey":57,"title":"Schedule Boost Rules for Campaigns"}]
event: text
data: {"delta":"Open the dataset's Boost rules tab and choose New boost rule [1]."}
event: done
data: {"model":"anthropic/claude-sonnet-5.5","askId":"9b1c…","latencyMs":3480}| Event | Data |
|---|---|
status | { step }: what the answer is doing, for a line such as "Searching…" |
sources | The records the answer may use, numbered; [n] in the text cites them |
filters | On a catalogue: the filters, words and sort it searched with, to put the same list on the page |
text | { delta }: the next part of the answer, in markdown |
done | { model, askId, latencyMs } |
error | { code, message }: nothingFound, upstreamBusy or failed |
history carries the conversation for a follow-up: up to three earlier turns of { question, answer }. A question is 1 to 1,000 characters. Send "proactive": true when the page starts the answer on its own, in the proactive mode, rather than the visitor asking: it counts against the day's proactive answers, and a dataset in another mode answers 429. The response's Indx-Ask-Id header names the answer from the first byte: send it as the queryId of a select when the visitor opens a cited source.
Turn a description into filters
On a catalogue, when intent reports a strong criteria, filters turns the text into filters on the dataset's own fields: a value or a number range on a Filterable field, checked against the values it holds. text is what no filter covers, to search for.
POST /api/teams/{teamName}/datasets/{dataSetName}/search-or-ask/filters
Content-Type: application/json
{ "text": "funny space movies from the 80s" }{
"filters": [
{ "field": "genres", "value": "Comedy" },
{ "field": "release_year", "min": 1980, "max": 1989 }
],
"text": "space",
"sort": null,
"ms": 1460
}Settings
GET returns the dataset's settings, what it runs with after defaults (effective: the site description, the fields the judge reads, the Ask phrases in effect, the model the answers are written with, how a catalogue's questions are searched, and the models the instance offers as answerModels) and whether the instance can run it (available). A Search key reads them without knowledge. PUT replaces them (a Full key) and applies from the next call; DELETE puts every value back to its default. PUT …/shown with { "shown": true } is the team admin's switch.
| Setting | Default | Meaning |
|---|---|---|
enabled | false | Search or ask is on for visitors |
kind | documentation | documentation or catalogue |
siteDescription | the dataset's description | Who reads the site, what the records hold |
titleField, sectionFields, snippetFields | automatic | What the judge reads of a result |
filterNotes | none | What values mean, one per line (catalogue) |
knowledge | none | Markdown facts no record holds; answers may state them, up to 8,000 characters |
answers | best | best or fast: the instance's answer model or its fast one |
answerModel | none | Another model the instance offers, by its id (one of effective.answerModels), in place of the one answers names. A model the instance stops offering answers with the one answers names |
searches | follows answers | On a catalogue, several (an agent that searches, sorts and filters until it has the answer) or one (one search and a summary of what it found). Unset, best searches several times and fast once |
answerInstructions | none | Voice and house rules, up to 1,500 characters |
answerLanguage | the visitor's | One language for every answer |
askPhrases | English defaults | { leadIns, frames, links, articles } |
refocusWhileTyping | true | The list searches what a question is about |
inviteOnAskPhrase | true | The invitation shows as soon as a phrase matches |
mode | soft | How forward the offer is: strict, soft, open, inviting or proactive (see the guide) |
dailyProactiveAnswers | 500 | Answers proactive writes without a click per UTC day; past it the mode is inviting until midnight |
thresholds | see above | strong, strongHide, invite, inviteHide; hint, where the inviting modes' hint starts (null: 0.25 on a catalogue, 0.31 on documentation); finished, 0.5, the score proactive needs before it answers on its own |
cutWhileAsking, cutShareOfTop, cutTopFloor | true, 0.5, 0.2 | Keep only the strong matches while the full offer is up |
strictCoverageWhileAsking | true | Whole words only on the page's searches while asking |
filtersWhileTyping | off | off, facets or model (catalogue) |
dailyCallBudget | 5000 | Calls per UTC day; 0 stops all calls |
Limits
A call over the per-address limit, or over the dataset's daily budget, answers 429 with Retry-After and retryAfterSeconds; the budget's code is searchOrAskBudgetSpent, and it resets at midnight UTC.
Statistics
GET /api/teams/{teamName}/datasets/{dataSetName}/statistics/search-or-ask?days=30A Read key. Offers shown and taken, questions asked and how they ended, the answers the proactive mode wrote on its own (proactiveAnswers, apart from asks), cited sources opened, what it cost, and the calls against the budget, with the questions the content did not answer first. Pass ?session=, ?source= and ?queryId= on intent and ask as on searches, so the statistics tie them to the searches they came from.
SearchOrAsk
What the dataset sets, what it runs with (the site description and fields after defaults), and whether this instance can run it at all (`available`: an API key is configured). A search key may read it: a search page needs to know whether to ask, and the thresholds.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
Replaces the dataset's settings. Applies from the next call; no reindex.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
| Content Type | Schema |
|---|---|
application/json | object |
Removes the dataset's settings: the feature is off and every value back to its default.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
Whether this dataset has Search or ask (its Options tab): a team admin's switch. Experimental, so off by default; on, the dataset's tab and its visitor routes appear, and `enabled` in the settings then takes it live.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
| Content Type | Schema |
|---|---|
application/json | object |
Judges what a visitor has typed so far: `p`, the probability they are asking for something an answer would serve, `answerable`, whether the results could answer it, and the `offer` those come to on their own. Call it on a typing pause, beside the search the page already ran. A judge that is slow or failing answers `p` 0 with a `reason`; it never fails the call. With `prefetch` true, call it while the visitor types as well, one call in flight at a time: the judge reads the text ahead of the pause and keeps its answer, so the pause's own call comes back from that at once. Only the pause's call is a judgement in the statistics.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
| Content Type | Schema |
|---|---|
application/json | object |
A catalogue's description of what to list, as filters on the dataset's own fields: call it when intent's `criteria` reaches the strong threshold. Each filter is a value or a number range on a Filterable field, checked against the values the dataset holds; a text field also needs to be Facetable, which is where its values come from. `text` is what no filter covers, to search for.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
| Content Type | Schema |
|---|---|
application/json | object |
Answers a question from the dataset's records, as server-sent events: `sources` (the records the answer may use, numbered), `text` (the answer as it is written, citing sources as [n]), then `done`; or `error` with a code. The answer uses those records and nothing else, and says so when they do not hold it.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
api-version | header | string | No |
api-version | query | string | No |
| Content Type | Schema |
|---|---|
application/json | object |
What Search or ask did in the last `days` (today included, in the dataset's time zone): offers shown and taken, questions asked and how they ended, cited sources opened, and what it cost. `questions` lists the questions asked, the ones the content did not answer first; `offered` the texts an offer was shown beside (from the searches the page named), so a false offer can be found. Counted rows only, like every statistics read.
| Name | In | Type | Required |
|---|---|---|---|
teamName | path | string | Yes |
dataSetName | path | string | Yes |
days | query | int32 | No |
source | query | string | No |
limit | query | int32 | No |
api-version | header | string | No |
api-version | query | string | No |