> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/api-reference/search-tools.md).

# Search tools

## List All Search Tools

> List every search instance \`\`POST /api/v1/search\`\` can name, or with \`\`?kind=fetch\`\` every fetch instance.\
> \
> \`\`stored\`\` are the editable rows written through this API; \`\`config\`\` are the\
> config-file entries, which are still honored and are reported so the operator\
> can see the whole set, \`\`builtin\_fetch\`\` first among the fetch instances. Keys\
> are never returned, only \`\`last4\`\`.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"SearchToolsResponse":{"description":"Every search instance ``POST /api/v1/search`` can name, or every fetch instance, by where it came from.","properties":{"config":{"items":{"$ref":"#/components/schemas/ConfigSearchToolSchema"},"title":"Config","type":"array"},"stored":{"items":{"$ref":"#/components/schemas/StoredSearchToolSchema"},"title":"Stored","type":"array"}},"required":["stored","config"],"title":"SearchToolsResponse","type":"object"},"ConfigSearchToolSchema":{"description":"A search or fetch instance declared in the config file, or ``builtin_fetch``. Read-only here.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A search instance's enrichment fetch instance. Null means the fetch default enriches it.","title":"Fetch Tool"},"has_api_key":{"description":"Whether the config entry carries an API key. The key itself is not shown.","title":"Has Api Key","type":"boolean"},"kind":{"default":"search","description":"Whether this is a search or a fetch instance.","enum":["search","fetch"],"title":"Kind","type":"string"},"name":{"title":"Name","type":"string"},"provider":{"title":"Provider","type":"string"},"shadowed":{"default":false,"description":"True when a stored instance of the same name and kind overrides this entry.","title":"Shadowed","type":"boolean"}},"required":["name","provider","has_api_key"],"title":"ConfigSearchToolSchema","type":"object"},"StoredSearchToolSchema":{"description":"A runtime-stored search or fetch instance. The API key is never returned, only ``last4``.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A search instance's enrichment fetch instance. Null means the fetch default enriches it.","title":"Fetch Tool"},"kind":{"default":"search","description":"Whether this is a search or a fetch instance.","enum":["search","fetch"],"title":"Kind","type":"string"},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4"},"name":{"title":"Name","type":"string"},"options":{"additionalProperties":true,"title":"Options","type":"object"},"provider":{"title":"Provider","type":"string"},"shadows_config":{"default":false,"description":"True when a config-file search tool of the same name exists; the stored one is in effect.","title":"Shadows Config","type":"boolean"},"timeout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Timeout"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider"],"title":"StoredSearchToolSchema","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools":{"get":{"description":"List every search instance ``POST /api/v1/search`` can name, or with ``?kind=fetch`` every fetch instance.\n\n``stored`` are the editable rows written through this API; ``config`` are the\nconfig-file entries, which are still honored and are reported so the operator\ncan see the whole set, ``builtin_fetch`` first among the fetch instances. Keys\nare never returned, only ``last4``.","operationId":"search-tools-list_all_search_tools","parameters":[{"description":"Which instances to list: 'search' (the default) or 'fetch'.","in":"query","name":"kind","required":false,"schema":{"default":"search","description":"Which instances to list: 'search' (the default) or 'fetch'.","enum":["search","fetch"],"title":"Kind","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchToolsResponse"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List All Search Tools","tags":["search-tools"]}}}}
```

## Create Search Tool

> Add a search or fetch instance at runtime. Storing an API key requires OTARI\_SECRET\_KEY.\
> \
> Creating a second search instance, while the first is the in-loop default\
> only because it is the only one, first sets \`\`web\_search\_default\_tool\`\` to\
> the first in the same commit, and says so in the response, so that adding\
> an instance never turns in-loop search off. That runtime value wins over the\
> configuration file until it is cleared.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"CreateSearchToolRequest":{"description":"Create a stored search or fetch instance. ``api_key`` is write-only and requires OTARI_SECRET_KEY.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Backend endpoint. Omit to inherit the provider's default (searxng inherits web_search_url).","title":"Api Base"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Provider API key. Stored encrypted; never returned.","title":"Api Key"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For a search instance: the fetch instance that enriches its results, a configured or stored one or builtin_fetch. Omit it for the fetch default.","title":"Fetch Tool"},"kind":{"default":"search","description":"'search' or 'fetch'. It cannot change once created.","enum":["search","fetch"],"title":"Kind","type":"string"},"name":{"description":"Name callers pass as 'search_tool_name' or in /api/v1/search/{tool}, or that names a fetch instance. It contains no '/' or ':', is not builtin_fetch or none in any case, and is unique across search and fetch instances.","minLength":1,"pattern":"^[^/:]+$","title":"Name","type":"string"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Provider-native request fields used as defaults (e.g. exa's 'type', searxng's 'engines').","title":"Options"},"provider":{"description":"Provider id. GET /api/v1/search-tools/providers lists the search providers, and with ?kind=fetch the fetch providers.","title":"Provider","type":"string"},"timeout":{"anyOf":[{"exclusiveMinimum":0,"type":"number"},{"type":"null"}],"description":"Per-request timeout in seconds.","title":"Timeout"}},"required":["name","provider"],"title":"CreateSearchToolRequest","type":"object"},"CreatedSearchToolSchema":{"description":"A stored instance as created, with the default setting the create also stored, if any.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A search instance's enrichment fetch instance. Null means the fetch default enriches it.","title":"Fetch Tool"},"kind":{"default":"search","description":"Whether this is a search or a fetch instance.","enum":["search","fetch"],"title":"Kind","type":"string"},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4"},"name":{"title":"Name","type":"string"},"notice":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"What else the create changed, for the operator to read.","title":"Notice"},"options":{"additionalProperties":true,"title":"Options","type":"object"},"pinned_web_search_default_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Set when this create also stored web_search_default_tool, naming the search instance that was the in-loop default because it was the only one, so that adding a second does not turn in-loop search off. This runtime value wins over the configuration file until it is cleared.","title":"Pinned Web Search Default Tool"},"provider":{"title":"Provider","type":"string"},"shadows_config":{"default":false,"description":"True when a config-file search tool of the same name exists; the stored one is in effect.","title":"Shadows Config","type":"boolean"},"timeout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Timeout"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider"],"title":"CreatedSearchToolSchema","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools":{"post":{"description":"Add a search or fetch instance at runtime. Storing an API key requires OTARI_SECRET_KEY.\n\nCreating a second search instance, while the first is the in-loop default\nonly because it is the only one, first sets ``web_search_default_tool`` to\nthe first in the same commit, and says so in the response, so that adding\nan instance never turns in-loop search off. That runtime value wins over the\nconfiguration file until it is cleared.","operationId":"search-tools-create_search_tool","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSearchToolRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedSearchToolSchema"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Search Tool","tags":["search-tools"]}}}}
```

## List Search Providers

> List the providers a search or fetch tool may name, for the add-tool form.\
> \
> The list comes from the metadata any-search and any-fetch publish, so a\
> provider either library adds appears with no change to the gateway. Reports\
> per provider whether an API key is required, what endpoint a tool inherits\
> when it declares none, and the native options a tool may set. Providers that\
> exist only for tests are left out, and so is the fetch provider \`\`builtin\`\`,\
> which only the implicit \`\`builtin\_fetch\`\` tool uses.\
> \
> What belongs to this deployment rather than to the libraries, its own tools\
> on each provider and an endpoint a tool inherits from its settings, is\
> shown only to a caller who operates the deployment: the tool settings\
> reader withholds the same from anyone else.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"SearchProviderSchema":{"description":"One provider a search or fetch instance may name, for the add-tool form.\n\nOne schema for both capabilities: the fields they share, then each one's\nown, which are null on the other's entries.","properties":{"default_api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The endpoint a tool on this provider uses when it declares no api_base. Null means nothing supplies one, so an api_base is required. One that comes from the deployment's own settings, such as the web_search_url a searxng tool inherits, is shown only to a caller who operates the deployment: nothing else inherits it, an organization's key included.","title":"Default Api Base"},"doc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The provider's API documentation.","title":"Doc Url"},"formats":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"Fetch: the formats the page text comes back in.","title":"Formats"},"id":{"description":"Value to send as 'provider'.","title":"Id","type":"string"},"instances":{"description":"The names of this deployment's configured and stored tools on this provider, shown only to a caller who operates the deployment.","items":{"type":"string"},"title":"Instances","type":"array"},"key_in_url":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Search: true when the API key travels in the request URL.","title":"Key In Url"},"kind":{"description":"Whether this is a search provider or a fetch provider.","enum":["search","fetch"],"title":"Kind","type":"string"},"max_results":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Search: the most results one call can ask for.","title":"Max Results"},"max_urls_per_call":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Fetch: the most pages one call can fetch.","title":"Max Urls Per Call"},"options":{"anyOf":[{"items":{"$ref":"#/components/schemas/SearchProviderOptionSchema"},"type":"array"},{"type":"null"}],"description":"The native options a tool may set, under the provider's own names. Null when there is no schema for the provider yet, so a tool's options are passed unchecked.","title":"Options"},"query_in_url":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Search: true when the query travels in the request URL.","title":"Query In Url"},"renders_javascript":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Fetch: true when the provider runs a page's JavaScript before reading it.","title":"Renders Javascript"},"reports_final_url":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Fetch: true when the provider reports where a page was found after redirects. When false it answers with the URL as asked, so a redirect it follows is not checked against the domain policy.","title":"Reports Final Url"},"requires_api_base":{"description":"True when this provider has no endpoint of its own, so the tool must say where the backend is.","title":"Requires Api Base","type":"boolean"},"requires_api_key":{"description":"True when a tool on this provider must carry an API key.","title":"Requires Api Key","type":"boolean"},"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The library's tier for the provider.","title":"Tier"}},"required":["id","kind","requires_api_key","requires_api_base"],"title":"SearchProviderSchema","type":"object"},"SearchProviderOptionSchema":{"description":"One native option a provider accepts, under the provider's own name.","properties":{"default":{"description":"The value the provider uses when the option is not set.","title":"Default"},"description":{"default":"","title":"Description","type":"string"},"enum":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"description":"The only values it takes, when it is limited to a list.","title":"Enum"},"name":{"title":"Name","type":"string"},"operator_only":{"default":false,"description":"True when only an instance or a credential may set it, never a workspace or a request.","title":"Operator Only","type":"boolean"},"type":{"enum":["string","integer","number","boolean","array","object"],"title":"Type","type":"string"}},"required":["name","type"],"title":"SearchProviderOptionSchema","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools/providers":{"get":{"description":"List the providers a search or fetch tool may name, for the add-tool form.\n\nThe list comes from the metadata any-search and any-fetch publish, so a\nprovider either library adds appears with no change to the gateway. Reports\nper provider whether an API key is required, what endpoint a tool inherits\nwhen it declares none, and the native options a tool may set. Providers that\nexist only for tests are left out, and so is the fetch provider ``builtin``,\nwhich only the implicit ``builtin_fetch`` tool uses.\n\nWhat belongs to this deployment rather than to the libraries, its own tools\non each provider and an endpoint a tool inherits from its settings, is\nshown only to a caller who operates the deployment: the tool settings\nreader withholds the same from anyone else.","operationId":"search-tools-list_search_providers","parameters":[{"description":"Which providers to list: search providers (the default) or fetch providers.","in":"query","name":"kind","required":false,"schema":{"default":"search","description":"Which providers to list: search providers (the default) or fetch providers.","enum":["search","fetch"],"title":"Kind","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SearchProviderSchema"},"title":"Response Search-Tools-List Search Providers","type":"array"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Search Providers","tags":["search-tools"]}}}}
```

## Reencrypt Stored Search Tool Keys

> Re-encrypt stored search-tool keys with the primary OTARI\_SECRET\_KEY.\
> \
> The search-tool half of the \`\`OTARI\_SECRET\_KEY\`\` rotation procedure; run it\
> alongside \`\`POST /api/v1/hosted-providers/reencrypt\`\`. Rows that cannot be\
> decrypted are left untouched and must be recovered by replacing the affected\
> tool's key.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ReencryptSearchToolsResponse":{"description":"Result of re-encrypting stored search-tool keys with the primary secret key.","properties":{"reencrypted":{"description":"Number of stored search-tool keys re-encrypted.","title":"Reencrypted","type":"integer"},"skipped":{"default":0,"description":"Number of rows whose stored key changed between the read and the write, so the re-encryption was not applied. They already hold whoever wrote them last.","title":"Skipped","type":"integer"},"unreadable":{"description":"Number of encrypted keys left untouched because they could not be decrypted.","title":"Unreadable","type":"integer"}},"required":["reencrypted","unreadable"],"title":"ReencryptSearchToolsResponse","type":"object"}}},"paths":{"/api/v1/search-tools/reencrypt":{"post":{"description":"Re-encrypt stored search-tool keys with the primary OTARI_SECRET_KEY.\n\nThe search-tool half of the ``OTARI_SECRET_KEY`` rotation procedure; run it\nalongside ``POST /api/v1/hosted-providers/reencrypt``. Rows that cannot be\ndecrypted are left untouched and must be recovered by replacing the affected\ntool's key.","operationId":"search-tools-reencrypt_stored_search_tool_keys","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReencryptSearchToolsResponse"}}},"description":"Successful Response"}},"summary":"Reencrypt Stored Search Tool Keys","tags":["search-tools"]}}}}
```

## Test Unsaved Search Tool

> Test an instance before saving it, with one search or one fetch.\
> \
> Takes the create request's fields, held to the create's checks against the\
> instances this worker has loaded, plus \`\`query\`\` for a search instance or\
> \`\`url\`\` for a fetch instance. Answers whether the provider answered without\
> an error, the error's tag when it did not, and how many hits or characters\
> came back, never the results or the page.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"SearchToolTestRequest":{"description":"An unsaved search or fetch instance to test, and what to test it with.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Backend endpoint. Omit to inherit the provider's default (searxng inherits web_search_url).","title":"Api Base"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Provider API key. Stored encrypted; never returned.","title":"Api Key"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For a search instance: the fetch instance that enriches its results, a configured or stored one or builtin_fetch. Omit it for the fetch default.","title":"Fetch Tool"},"kind":{"default":"search","description":"'search' or 'fetch'. It cannot change once created.","enum":["search","fetch"],"title":"Kind","type":"string"},"name":{"description":"Name callers pass as 'search_tool_name' or in /api/v1/search/{tool}, or that names a fetch instance. It contains no '/' or ':', is not builtin_fetch or none in any case, and is unique across search and fetch instances.","minLength":1,"pattern":"^[^/:]+$","title":"Name","type":"string"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Provider-native request fields used as defaults (e.g. exa's 'type', searxng's 'engines').","title":"Options"},"provider":{"description":"Provider id. GET /api/v1/search-tools/providers lists the search providers, and with ?kind=fetch the fetch providers.","title":"Provider","type":"string"},"query":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"For a search instance: the query to run.","title":"Query"},"timeout":{"anyOf":[{"exclusiveMinimum":0,"type":"number"},{"type":"null"}],"description":"Per-request timeout in seconds.","title":"Timeout"},"url":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"For a fetch instance: the page to fetch.","title":"Url"}},"required":["name","provider"],"title":"SearchToolTestRequest","type":"object"},"SearchToolTestResponse":{"description":"How one search or one fetch went. Never the results or the page.","properties":{"characters":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For a fetch that worked: how many characters of page text came back.","title":"Characters"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When not ok, the error's tag: timeout, network, http_error, invalid_response, unexpected_response (an answer whose shape the provider changed), or the provider's own.","title":"Error"},"hits":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For a search that worked: how many hits came back.","title":"Hits"},"ok":{"description":"Whether the provider answered the call without an error.","title":"Ok","type":"boolean"}},"required":["ok"],"title":"SearchToolTestResponse","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools/test":{"post":{"description":"Test an instance before saving it, with one search or one fetch.\n\nTakes the create request's fields, held to the create's checks against the\ninstances this worker has loaded, plus ``query`` for a search instance or\n``url`` for a fetch instance. Answers whether the provider answered without\nan error, the error's tag when it did not, and how many hits or characters\ncame back, never the results or the page.","operationId":"search-tools-test_unsaved_search_tool","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchToolTestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchToolTestResponse"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Test Unsaved Search Tool","tags":["search-tools"]}}}}
```

## Update Search Tool

> Update a stored search or fetch instance. Omitted fields are left as-is; an explicit \`\`null\`\` clears them.\
> \
> \`\`api\_key\`\` follows the same rule: omit it to keep the stored key, send a new\
> one to rotate, or send \`\`null\`\` to clear it (a keyless SearXNG backend). The\
> row is locked \`\`FOR UPDATE\`\` so the \`\`expected\_updated\_at\`\` check and the\
> write it guards are atomic. The tool as it will be after the update is\
> validated, so a change that would leave it unusable (clearing the key of a\
> provider that needs one) is refused rather than stored. Its options are\
> checked against the provider's when the update sets them or changes the\
> provider, and \`\`fetch\_tool\`\` when the update sets it, so rotating the key of\
> an instance stored before those rules never trips on them. \`\`kind\`\` cannot\
> change, so an instance never moves between the search and fetch maps.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"UpdateSearchToolRequest":{"description":"Update a stored search or fetch instance. Omitted fields are unchanged; ``api_key`` rotates in place.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"New API key. Omit to keep the existing one. Never returned.","title":"Api Key"},"expected_updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optimistic concurrency: if set, the update 412s unless it matches the stored updated_at.","title":"Expected Updated At"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For a search instance: the fetch instance that enriches its results. Null clears it.","title":"Fetch Tool"},"kind":{"anyOf":[{"enum":["search","fetch"],"type":"string"},{"type":"null"}],"description":"Accepted only when it matches the stored kind, which cannot change.","title":"Kind"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Options"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"timeout":{"anyOf":[{"exclusiveMinimum":0,"type":"number"},{"type":"null"}],"title":"Timeout"}},"title":"UpdateSearchToolRequest","type":"object"},"StoredSearchToolSchema":{"description":"A runtime-stored search or fetch instance. The API key is never returned, only ``last4``.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"fetch_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A search instance's enrichment fetch instance. Null means the fetch default enriches it.","title":"Fetch Tool"},"kind":{"default":"search","description":"Whether this is a search or a fetch instance.","enum":["search","fetch"],"title":"Kind","type":"string"},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4"},"name":{"title":"Name","type":"string"},"options":{"additionalProperties":true,"title":"Options","type":"object"},"provider":{"title":"Provider","type":"string"},"shadows_config":{"default":false,"description":"True when a config-file search tool of the same name exists; the stored one is in effect.","title":"Shadows Config","type":"boolean"},"timeout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Timeout"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider"],"title":"StoredSearchToolSchema","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools/{name}":{"patch":{"description":"Update a stored search or fetch instance. Omitted fields are left as-is; an explicit ``null`` clears them.\n\n``api_key`` follows the same rule: omit it to keep the stored key, send a new\none to rotate, or send ``null`` to clear it (a keyless SearXNG backend). The\nrow is locked ``FOR UPDATE`` so the ``expected_updated_at`` check and the\nwrite it guards are atomic. The tool as it will be after the update is\nvalidated, so a change that would leave it unusable (clearing the key of a\nprovider that needs one) is refused rather than stored. Its options are\nchecked against the provider's when the update sets them or changes the\nprovider, and ``fetch_tool`` when the update sets it, so rotating the key of\nan instance stored before those rules never trips on them. ``kind`` cannot\nchange, so an instance never moves between the search and fetch maps.","operationId":"search-tools-update_search_tool","parameters":[{"in":"path","name":"name","required":true,"schema":{"title":"Name","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSearchToolRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoredSearchToolSchema"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Search Tool","tags":["search-tools"]}}}}
```

## Delete Stored Search Tool

> Delete a stored search or fetch instance. A config-file one, or \`\`builtin\_fetch\`\`, cannot be deleted here.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools/{name}":{"delete":{"description":"Delete a stored search or fetch instance. A config-file one, or ``builtin_fetch``, cannot be deleted here.","operationId":"search-tools-delete_stored_search_tool","parameters":[{"in":"path","name":"name","required":true,"schema":{"title":"Name","type":"string"}}],"responses":{"204":{"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Stored Search Tool","tags":["search-tools"]}}}}
```

## Test Search Tool

> Test a configured or stored instance with one search or one fetch.\
> \
> Takes \`\`query\`\` for a search instance or \`\`url\`\` for a fetch instance, and\
> answers as \`\`POST /search-tools/test\`\` does. \`\`builtin\_fetch\`\`, the built-in\
> fetcher, fetches as a fetch tool call does, with no domain policy.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"StoredSearchToolTestRequest":{"description":"What to test a configured or stored instance with.","properties":{"query":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"For a search instance: the query to run.","title":"Query"},"url":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"description":"For a fetch instance: the page to fetch.","title":"Url"}},"title":"StoredSearchToolTestRequest","type":"object"},"SearchToolTestResponse":{"description":"How one search or one fetch went. Never the results or the page.","properties":{"characters":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For a fetch that worked: how many characters of page text came back.","title":"Characters"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When not ok, the error's tag: timeout, network, http_error, invalid_response, unexpected_response (an answer whose shape the provider changed), or the provider's own.","title":"Error"},"hits":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For a search that worked: how many hits came back.","title":"Hits"},"ok":{"description":"Whether the provider answered the call without an error.","title":"Ok","type":"boolean"}},"required":["ok"],"title":"SearchToolTestResponse","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/search-tools/{name}/test":{"post":{"description":"Test a configured or stored instance with one search or one fetch.\n\nTakes ``query`` for a search instance or ``url`` for a fetch instance, and\nanswers as ``POST /search-tools/test`` does. ``builtin_fetch``, the built-in\nfetcher, fetches as a fetch tool call does, with no domain policy.","operationId":"search-tools-test_search_tool","parameters":[{"in":"path","name":"name","required":true,"schema":{"title":"Name","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoredSearchToolTestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchToolTestResponse"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Test Search Tool","tags":["search-tools"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/api-reference/search-tools.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
