API Keys
Manage AACsearch API keys: scopes, creation, rotation, revocation, origin allow-lists, rate limits, and the security model.
API keys authenticate every request to the public Search API, ingest pipeline, and Connector API. The dashboard exposes two views of keys:
/[orgSlug]/api-keys— all keys across all indexes in the organization (administrative view)./[orgSlug]/search→ API Keys tab — keys for a single index (operator view, scoped to one index at a time).
This page covers both views. For request-level use (headers, errors, rate-limit semantics) see Search API → Public search endpoint.
Key types and prefixes
Every key carries a typed prefix. The prefix is part of the key string and is stored in plaintext alongside its hash.
| Prefix | Type | Where it is used |
|---|---|---|
ss_search_* | Public search key | Browser SDK, widget, server-side search calls |
ss_connector_* | Connector token | CMS modules, server-to-server ingest |
ss_scoped_* | Scoped token | Short-lived, signed tokens that AND-combine filters (multi-tenant) |
aa_admin_* | Admin key | Server-only ingest and management against the v1 REST surface |
Admin keys (aa_admin_*) are not created in the dashboard — they are provisioned out of band and live only on trusted servers.
Key scopes
A key has one or more scopes that decide which endpoints it can reach.
| Scope | Grants access to |
|---|---|
search | Read-only search queries. Safe to embed in browsers. |
ingest | Submit, update, delete documents via the REST ingest endpoints. |
admin | Create/delete indexes, edit schema, run reindex, manage all keys. |
connector_write | Used by CMS connector modules to push catalog sync deltas. |
Pick the narrowest scope that does the job. A widget on a marketing site needs only search. A Shopify sync worker needs only connector_write. Reserve admin for trusted operators and CI.
How keys are stored
AACsearch never stores plaintext keys. At creation time:
- The server generates the random secret part.
- The full key (prefix + secret) is shown to the user exactly once.
- Only
SearchApiKey.prefix,SearchApiKey.hash(SHA-256), and metadata (name, scopes, allowed origins, rate limit, expiry) are persisted.
If you lose the plaintext, you cannot recover it. Create a new key and revoke the old one.
The database model lives in packages/database/prisma/schema.prisma as SearchApiKey. Plaintext is verified against the hash on every request by packages/api/modules/search/lib/public-auth.ts.
Creating a key
From /[orgSlug]/search → API Keys tab:
- Select the target index from the dropdown.
- Click Create Key.
- Enter a human-readable name (e.g.
storefront-widget-prod). - Pick one or more scopes.
- Optional: set an expiry date.
- Optional: set allowed origins (browser CORS gate, see below).
- Optional: override the per-key rate limit (default: 600 requests per minute).
After clicking Create, the plaintext key is shown once. Copy it into your secret store (Vault, AWS Secrets Manager, etc.) before closing the dialog.
Allowed origins (CORS allow-list)
For ss_search_* keys destined for browser use, you can pin them to a list of origins. Requests from any other origin are rejected with HTTP 403.
- Use
https://shop.example.comfor production storefronts. - Add
http://localhost:3000for local development. - Leave empty to disable the allow-list (the key works from any origin — useful for server-side keys but never for browser keys).
The allowed origins check happens before the rate-limit and quota gates. It is the first line of defense for keys that leak into a frontend bundle.
Rate limits per key
Every key has its own sliding-window rate limit, default 600 requests per minute. Exceeding the limit returns HTTP 429 with error: "rate_limit_exceeded" and a Retry-After header.
The bucket lives in SearchRateLimitBucket keyed by (keyId, windowStart). Tighten the limit on keys you suspect of abuse without revoking them — useful for triage.
See Search API → Errors and rate limits for headers, response shape, and the global org-level quota that sits behind per-key limits.
Revoking a key
Click Revoke on any active key. Revocation is immediate — the next request using the key is rejected. Revoked keys remain visible in the list with a strikethrough until 90 days after revocation, then they are soft-deleted (deletedAt).
Revoke a key when:
- It has been committed to a public repository or otherwise leaked.
- An employee with access to the secret has left.
- A connector has been decommissioned.
- You are rotating keys on a routine schedule.
Key rotation
There is no in-place rotation. The flow is create → swap → revoke:
- Create a new key with the same scopes.
- Deploy the new key to the consumer (widget, worker, server).
- Confirm traffic on the new key in the Activity tab of Analytics.
- Revoke the old key.
This ensures zero downtime. For browser widgets, deploy a new build before revoking the old key — otherwise users with cached HTML continue to use the revoked key and see search errors.
Scoped search tokens
Scoped tokens (ss_scoped_*) are short-lived, signed tokens minted from a parent search key. They carry a filter that is AND-combined with any client-supplied filter. The token can narrow access (e.g. "only show this user's documents") but never widen it.
You do not create scoped tokens from this page. They are minted at request time by your server using verifyScopedSearchToken(). See Scoped Search Tokens for the full flow.
Audit trail
Every key creation, revocation, and scope change is logged to the audit log:
| Audit action | When emitted |
|---|---|
create_api_key | New key minted |
revoke_api_key | Key revoked |
create_scoped_token | Scoped token minted (server-side log) |
See Audit Logs for filtering, retention, and export.
Common errors
| Status | Error code | Meaning |
|---|---|---|
| 401 | invalid_api_key | Hash does not match any stored key |
| 401 | api_key_revoked | Key exists but revokedAt is set |
| 401 | api_key_expired | Key exists but expiresAt is in the past |
| 403 | origin_not_allowed | Request Origin not in allowedOrigins |
| 403 | scope_insufficient | Key valid but missing required scope for the endpoint |
| 429 | rate_limit_exceeded | Per-key rate limit exceeded |
| 429 | search_quota_exceeded | Org-level monthly quota exhausted |
Related
- Search Workspace — index-level controls including the per-index API Keys tab.
- Search API → Public search endpoint — request and response shape.
- Scoped Search Tokens — multi-tenant filtering via signed tokens.
- Errors and Rate Limits — full HTTP error catalog.
- Audit Logs — who did what, when.