Product Search
Captain Product 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. Product 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 Product 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. Keep all three on your server. In a browser, use a scoped key restricted to your store’s origins, as the Search widget guide shows. Access-control and audit settings go through Captain. Index settings lists what you set and what Captain sets.
Only a search key reads analytics. A scoped, issuer or write key gets 403. On an index with per-record access control, analytics need Captain’s admin key, so Captain provides them.
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.
Create Key takes a write key for every index or an issuer key. Any other key gets 403.
A key can also carry expires_at (unix seconds) and allowed_origins, the sites a browser may use it from.
Mint a scoped key with the issuer key
The issuer key you got at onboarding does one thing: it mints scoped keys. Use it on your server to give each visitor or signed-in user a short-lived key. A key it mints must be scoped, must name its indexes, and must carry an expires_at within the issuer’s max_ttl (one hour unless it was set otherwise). The issuer’s own filter is added to every key it mints. Its allowed_origins carry over too, unless the request sends its own.
expires_at here is one hour from now, the longest an issuer key allows by default.
The reply has the same shape as above, with issued_by naming the issuer key. Revoking the issuer key revokes every key it minted.
List and revoke keys
List Keys (GET /v1/keys) returns every key without its secret, and marks the key making the call with current: true. Get Key (GET /v1/keys/{key_id}) returns one key. 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. Revoking the key that makes the call answers 409 KEY_IS_CALLER. 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 lists, gets and revokes keys. A write key limited to some indexes gets 403, and so do search, scoped and issuer keys.
On a tenant served from several regions, keys are created and revoked in its primary region. A Create Key or Revoke Key request that reaches another region answers 409 KEYS_READ_ONLY, and its message names the primary region. A key created there works in every region within a few seconds.
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 scoped key can be used straight from a web page. A search key works from a browser too, but it reads your analytics, so keep it on your server. 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. Set that list in allowed_origins when you create the key with Create 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.