App Search
Captain App Search powers the search box inside an application or website: product search on a store, listing search on a marketplace, article search on a help center, or record search inside a SaaS product.
Captain File Search is a separate product for files and their contents. It uses collections, documents and chunks. App Search uses indexes and records.
Vocabulary
How a request finds its data
Each customer gets a dedicated deployment and a dedicated hostname, for example https://acme.captain.dev.
- The hostname selects the tenant.
- The path selects the index:
/v1/indexes/products/search. - The key grants access to that tenant’s indexes and operations.
Requests carry no tenant id, and there is no /tenants/... path. Two tenants may each have an index called products. A key issued for one tenant does not work against another tenant’s endpoint.
What Captain manages
Captain creates each tenant and its keys, and tunes ranking and index settings with the customer. The customer’s application creates indexes, keeps their records up to date, searches, and sends click and conversion events. Keys lists which key calls which operation.
Endpoint
Onboarding provides two things: the tenant endpoint, for example https://acme.captain.dev, and the keys for this tenant. The Quickstart creates a first index from there.
Every App Search route sits under that endpoint:
Keys
Captain issues a search key, a write key and an issuer key at onboarding, each for every index, and keeps the admin key. Access-control and audit settings go through Captain. Index settings lists what you set and what Captain sets.
Which key calls which operation
A tenant’s keys work on every index in that tenant, including indexes created later. A key can also be limited to some indexes. A request for any other index returns 403, the same answer as for an index that does not exist.
Manage your keys
The write key you got at onboarding covers every index, and it manages keys too. Create Key mints a search, scoped, write or issuer key, for every index (["*"]) or for the indexes you name. The new key’s secret is in that reply only. Captain stores a hash and can’t show it again.
A key can also carry expires_at (unix seconds) and allowed_origins. List Keys (GET /v1/keys) returns every key without its secret, and marks the key making the call with current: true. Revoke Key (DELETE /v1/keys/{key_id}) stops a key at once.
Revoking a key also revokes every key it created, and every key those created. The reply’s revoked lists every id it revoked. To revoke only the one key, send ?cascade=none.
A key can’t revoke itself, so you can’t lock yourself out. To replace a leaked write key:
- Get a new write key for every index that the old key didn’t create: ask Captain support for one, or mint it with another write key you trust. A key the old key minted would be revoked with it, so revoking the old key with such a key answers
409 CASCADE_INCLUDES_CALLERand revokes nothing. - With the new key, mint replacements for every key the old key minted that your apps still use, and deploy them. Revoking the old key in the next step revokes those keys too, so an app still on one of them stops working.
- Revoke the old key with the new one. That also revokes every key the old key minted, including any an attacker minted with it. To revoke only the old key and keep the keys it minted, send
?cascade=none, but then any key an attacker minted keeps working until it’s revoked on its own.
Only a write key for every index manages keys. A write key limited to some indexes gets 403.
List Keys also shows keys Captain issued on your tenant, such as a short-lived key support uses to look at your index with you. Those carry issued_by_captain: true, and revoking one with your key answers 403 KEY_ISSUED_BY_CAPTAIN. Ask support to revoke it.
Sending the key
Every endpoint takes its key the same way, as a bearer token in the Authorization header. Each endpoint’s reference page names the kind of key it needs.
curl
Python
TypeScript
A missing or unknown key returns 401. A key without access to the index or operation returns 403, and so does an index the key cannot see, whether or not that index exists.
Calling from a browser
A search key, and a scoped key once minted, can be used straight from a web page. Captain answers the browser’s preflight for any origin, without a key. A page may send these request headers:
Authorization(orX-Captain-Key)Content-TypeX-Captain-Min-Seq
Answers to a search or scoped key allow the page’s origin and let page code read X-Captain-Seq, X-Captain-Applied-Seq and X-Request-Id.
A key can be limited to your own sites. Captain sets that list when it issues the key, and a request from any other origin then gets 403 ORIGIN_NOT_ALLOWED. The origin check stops other websites from using your key in their visitors’ browsers. It doesn’t stop a server that sends a fake origin. Limit what a leaked key can reach with its scope, and revoke it if it leaks.
Write keys stay on your server. Their answers carry no CORS headers, so a page can’t read them.
Errors
Every error reply has the same shape: a machine code in error and a readable message. That includes a path no endpoint answers (404 ROUTE_NOT_FOUND) and a method an endpoint doesn’t take (405 METHOD_NOT_ALLOWED).
A request whose connection closes before any answer arrives never reached Captain. Connections and retries shows when to send it again.
Limits
Machine-readable guides
Each tenant publishes its own reference:
openapi.json lists the endpoints in this reference, as deployed on that tenant. llms.txt is a short plain-text guide for coding assistants.
Request ids
Every reply carries an X-Request-Id header. Quote it when you contact Captain about a request. A request that sends its own X-Request-Id (8 to 128 letters, digits, ., _ or -) gets the same id back.