AACsearch
Dashboard & Operations

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.

Registerkarte API-Schlüssel pro Index mit Präfixen, Bereichen und Ratenbegrenzungen

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.

PrefixTypeWhere it is used
ss_search_*Public search keyBrowser SDK, widget, server-side search calls
ss_connector_*Connector tokenCMS modules, server-to-server ingest
ss_scoped_*Scoped tokenShort-lived, signed tokens that AND-combine filters (multi-tenant)
aa_admin_*Admin keyServer-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.

ScopeGrants access to
searchRead-only search queries. Safe to embed in browsers.
ingestSubmit, update, delete documents via the REST ingest endpoints.
adminCreate/delete indexes, edit schema, run reindex, manage all keys.
connector_writeUsed 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:

  1. The server generates the random secret part.
  2. The full key (prefix + secret) is shown to the user exactly once.
  3. 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]/searchAPI Keys tab:

  1. Select the target index from the dropdown.
  2. Click Create Key.
  3. Enter a human-readable name (e.g. storefront-widget-prod).
  4. Pick one or more scopes.
  5. Optional: set an expiry date.
  6. Optional: set allowed origins (browser CORS gate, see below).
  7. 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.com for production storefronts.
  • Add http://localhost:3000 for 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:

  1. Create a new key with the same scopes.
  2. Deploy the new key to the consumer (widget, worker, server).
  3. Confirm traffic on the new key in the Activity tab of Analytics.
  4. 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 actionWhen emitted
create_api_keyNew key minted
revoke_api_keyKey revoked
create_scoped_tokenScoped token minted (server-side log)

See Audit Logs for filtering, retention, and export.

Common errors

StatusError codeMeaning
401invalid_api_keyHash does not match any stored key
401api_key_revokedKey exists but revokedAt is set
401api_key_expiredKey exists but expiresAt is in the past
403origin_not_allowedRequest Origin not in allowedOrigins
403scope_insufficientKey valid but missing required scope for the endpoint
429rate_limit_exceededPer-key rate limit exceeded
429search_quota_exceededOrg-level monthly quota exhausted

On this page