AACSearch / guide
API keys, scopes, restrictions and credits
Which key goes where, what a browser key may call, and how AI operations are paid for.
Every request to the tenant API carries an organization API key in the
X-NOVA-API-KEY header. A key has one of two scopes.
Full keys
A full key reaches every operation in the API reference and belongs on your server: indexing and importing documents, configuring collections, synonyms, curation, search rules, recommendation models and assistants, reading analytics, uploading media, generating embeddings.
Search keys — the key for a browser
A search key is the one to ship in a storefront page, a mobile app or a
widget. It reaches only the operations marked x-nova-key-scope: search in the
contract:
| Operation | What a visitor does with it |
|---|---|
searchCollection, multiSearch | search, autocomplete, facets |
getRecommendations | similar items, trending, "for you" |
ingestEvents | search, click, add-to-cart and purchase events |
searchSemantic | "similar in meaning" when keywords find nothing |
searchByImage | search by photo |
createTranscription, searchByVoice | voice input |
createChatCompletion, streamChatCompletion | the support assistant |
getConversation, deleteConversation | the visitor's own conversation |
suggestQueries, presearchQueries | typeahead and before-typing suggestions |
createSupportThread, getSupportThread, createSupportMessage | the visitor's own support thread |
listGraphEntities | read the catalogue's entity graph |
getSearchProfileView | the browser-safe projection of a search profile (sorts, facets, default sort, page-size ceiling) |
ingestAnalyticsEvents | a small fixed-shape batch of search/click/conversion events, sized for untrusted browser clients |
Everything else answers 403. A search key cannot write documents or read or
change configuration, so the worst a copied key can do is search.
The session header
Browser clients send X-NOVA-SESSION-ID, one id per browser tab. It is what
ties a visitor's events together for analytics and A/B tests, what a
conversation belongs to, and what a search key's AI allowance is counted
against. With a search key, AI operations require it (400 without); a
conversation started by one session cannot be read or continued by another
(403). The aacsearch-ui packages and the light widget send it for you.
Key restrictions
Scope decides what a key can call; restrictions decide which data it sees. Any key, full or search, can carry them: set them when you create the key in the console, or later from the key's Restrictions action. A restriction left empty means unrestricted.
| Restriction | What it limits | Refused with |
|---|---|---|
allowedCollections | The collections the key may name. Operations without a collection (health, keys) are governed by scope alone | 403 |
forcedFilterBy | A filter_by fragment per collection, or under * for every collection, ANDed into every search: document-level access control. When a collection has its own entry and * exists too, both apply | — (narrows results) |
allowedFields | Field-level access. include_fields is cut down to the allowed fields (a request whose whole selection is forbidden gets id only). Naming any other field in query_by, facet_by, sort_by, group_by, highlight_fields, include_fields, exclude_fields or filter_by is refused rather than silently rewritten. address also allows address.city; id and _-prefixed virtual fields are always allowed | 400 |
allowedIps | IPv4/IPv6 addresses or CIDR ranges the key may be used from | 403 |
allowedOrigins | Page origins, scheme://host[:port], with *.example.com for every subdomain. Checked against Origin, then Referer; a request with neither is refused, so don't set this on a key your server also calls with | 403 |
rateLimitPerMinute | Requests per minute for this key | 429 with Retry-After |
The forced filter is always ANDed with the caller's own filter_by, never
replaced by it:
// key: forcedFilterBy = { "products": "tenant_id:=42" }
GET /v1/collections/products/documents/search?q=drill&filter_by=in_stock:=true
// the engine receives
filter_by=(in_stock:=true) && (tenant_id:=42)Restrictions run before search rules and A/B experiments, so those can only narrow a restricted search further, never widen it.
Where they apply
Not only on a plain searchCollection. The same checks run on every read path
that returns documents:
multiSearch: each item is checked against its own collection; one item naming a disallowed collection refuses the whole batch with403.- The assistant (
createChatCompletion,streamChatCompletion): disallowed collections are dropped from retrieval (403when none is left), and sources carried over from earlier turns are re-checked against the current key. getRecommendations,suggestQueriesandpresearchQueries.searchSemantic,searchByImageandsearchByVoice.
Secured (child) keys
A browser key minted with createBrowserKey (POST /browser-keys, see
the widget's keys) is a signed nssk_… token derived from a
parent full key and never stored. It accepts allowed_collections,
allowed_origins, rate_limit_per_minute, user_token and
expires_in_seconds (at most seven days), is limited to the search scope,
and the parent's own restrictions still apply. The two are combined so a child
can only narrow its parent:
- Collections and fields intersect. Lists that share nothing deny everything; they never fall back to unrestricted.
- Forced filters merge per collection (and per
*): where both name the same entry, the child's fragment is ANDed with the parent's, so a child can add a filter but never replace one. - Rate limit: the lower of the two wins.
- Origins and IPs: a list the child sets is used in place of the parent's; otherwise the parent's applies.
Revoking or rotating the parent key invalidates every key derived from it.
Credits: how AI is paid for
Your organization has a wallet of credits (1 credit = 1 minor currency unit). Every model call is paid from it, whichever key made the request — the platform runs the models with its own provider keys and bills you in credits:
| Operation | Charged for |
|---|---|
| Assistant turn | tokens in and out |
| Transcription, voice search | minutes of audio |
Semantic search, POST /embeddings | texts embedded |
| Image search | images embedded |
| Synonym suggestions (speller) | queries the model was asked about |
Graph entity/claim extraction (generateGraphEntities) | documents processed |
Graph community reports (generateGraphCommunities) | communities summarized |
Graph search (globalSearchGraph, driftSearchGraph) | model calls — community reports mapped, DRIFT follow-ups answered, plus one reduce call |
Before a model runs, the cost is estimated and reserved; when it finishes, the
actual cost is charged and the rest of the reservation released. A wallet that
cannot cover the reservation answers 402 with what is available and what was
needed. A failed model call costs nothing. Each charge is a ledger entry you can
read from the console, named after the operation (assistant.chat,
voice.transcription, search.image, …).
A new organization's wallet opens with a small trial balance; plans add their included credits when they activate, and you can top the wallet up any time.
Quotas and allowances
Requests count against the plan's monthly quota (429 when it is used up).
Because a search key ships in a browser and can be copied out of it, AI
operations made with one are also limited per visitor session — a fixed
number per minute, after which the API answers 429 with a retry_after
value. Full keys are a server's and are not throttled that way.
Error responses
Every operation documents 401 (missing or revoked key), 403 (scope) and
429 (quota or allowance); the credit-metered operations also 402. The
generated SDKs raise a typed error for each — UnauthorizedError,
ForbiddenError, PaymentRequiredError, TooManyRequestsError — so a
storefront can show "assistant unavailable" instead of a stack trace.
Semantic, image and voice search
Search a collection by meaning in any language, find documents from a photo, search by voice, embed text and store images with signed URLs.
Plans, usage and credits
What each plan grants, how requests are counted, what an AI credit buys and costs, how RUB payments and renewals work, and what happens when a limit is reached.