Embed-Session
POST /api/v1/embed/session — tauscht einen API-Schlüssel serverseitig gegen ein kurzlebiges, reines Such-Session-Token, das sicher auf einer Seite stehen darf. Dazu die browserseitigen /embed/*-Endpunkte, die es akzeptieren.
POST /api/v1/embed/session
Authorization: Bearer aac_live_…Ein aac_live_…-Schlüssel darf niemals in einen Browser gelangen. Dieser Endpunkt existiert genau dafür: Er tauscht den Schlüssel gegen ein Embed-Session-Token, beschränkt auf einen Index, eine Aktion (search:read) und eine Laufzeit. Das Token kommt auf die Seite (data-aac-token); der Schlüssel bleibt auf Ihrem Server.
Ein GET /embed/bootstrap und eine ?key=-Variante gibt es nicht. Ein Schlüssel in einer Query-Zeichenfolge, einem data--Attribut oder im HTML der Seite ist genau das Leck, das dieses Design beseitigt: Der Endpunkt liest den Schlüssel nur aus dem Authorization-Header und antwortet sonst mit 401 UNAUTHORIZED.
Rufen Sie ihn aus Ihrem eigenen Backend auf — Route-Handler, SSR-Durchlauf oder Build-Schritt — und reichen Sie das resultierende token an die Seite weiter.
Session ausstellen
curl -sS -X POST "$AAC_API/embed/session" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{ "index": "documentation", "expires_in": 900 }'| Body-Feld | Typ | Standard | Bedeutung |
|---|---|---|---|
index | string ≤ 64 | Index des Schlüssels bzw. ältester Index des Mandanten | Index-Slug, auf den die Session beschränkt wird. Ein an einen Index gebundener Schlüssel kann keine Session für einen anderen ausstellen |
expires_in | Ganzzahl, Sekunden | 900 (15 Minuten) | Gewünschte Laufzeit, begrenzt auf 60–3600 |
Der Body ist optional. Ein nicht numerisches expires_in oder ein zu langer index ergibt 422 VALIDATION_ERROR.
{
"ok": true,
"token": "aac_sess_…",
"token_type": "embed_session",
"scope": ["search:read"],
"index": { "slug": "documentation", "name": "Documentation" },
"expires_at": "2026-10-23T11:15:02.000Z",
"expires_in": 900,
"settings": {
"placeholder": "Dokumentation durchsuchen…",
"perPage": 8,
"queryBy": "title,content",
"facets": ["category"],
"showStats": true,
"showRecent": true,
"showVoice": false,
"showImage": false,
"showRelated": false,
"accent": "#2563eb",
"dark": false,
"sortOptions": [
{ "label": "Relevanz", "value": "default" },
{ "label": "Neueste zuerst", "value": "created_at:desc" }
]
},
"ui": { "name": "AACSearch UI", "version": "1.0.0" },
"request_id": "req_4a6c8e0b2d4f6a8c0e2b4d6f"
}Was die Antwort garantiert:
- Das Token ist nur zum Suchen. Es trägt
search:readund sonst nichts, kann also weder schreiben noch löschen noch reindexieren. Wird die Seite kompromittiert, erbeutet man ein Credential, das von selbst abläuft und nur liest, was die Seite ohnehin anzeigt. - Das Token ist an einen Index und einen Mandanten gebunden. Der Mandantenfilter wird bei jeder Anfrage serverseitig angewendet; der Browser sieht weder einen Infrastrukturschlüssel noch Host oder Collection-Namen.
- Es läuft ab. Standard 900 Sekunden, höchstens 3600. Für langlebige Seiten rechtzeitig ein neues ausstellen; ein abgelaufenes Token ergibt
401 EMBED_SESSION_EXPIRED. - Es wird nicht gecacht. Antworten tragen
cache-control: no-store; jede Ausstellung landet im Audit-Log (embed.session.issue). - Einstellungen sind live.
settingsstammt ausSearchIndex.settings.embed; eine Änderung im Panel „Erscheinungsbild“ der Konsole ist beim nächsten Seitenaufruf sichtbar — ohne Redeploy.
Origin-Allowlist
Hat der Schlüssel allowedOrigins, ergibt ein Browser-Aufruf von einem anderen Origin 403 ORIGIN_NOT_ALLOWED und erhält keine CORS-Header. Eine leere Liste bedeutet „jeder Origin“. Da der Session-Endpunkt aus Ihrem Backend aufgerufen werden soll, trägt ein Server-zu-Server-Aufruf keinen Origin-Header und wird nicht geprüft.
Einstellungen
| Schlüssel | Typ | Standard | Wirkung |
|---|---|---|---|
placeholder | string | Search the docs… | Platzhalter des Eingabefelds |
perPage | number | 8 | Treffer pro Seite |
queryBy | string | title,content | query_by jeder Suche |
facets | string[] | [] | Facettenfelder als Checkbox-Listen (die Konsole erlaubt höchstens 6) |
showStats | boolean | true | Zeile mit der Trefferzahl |
showRecent | boolean | true | Chips mit letzten Anfragen, lokal pro Index gespeichert |
showVoice | boolean | false | Spracheingabe des Browsers über InstantSearch voiceSearch |
showImage | boolean | false | Trefferbild anzeigen, wenn das Dokument eines hat |
showRelated | boolean | false | „Für Sie“-Leiste unter den Ergebnissen (Empfehlungen müssen am Index aktiviert sein) |
accent | string | #2563eb | Akzentfarbe für Buttons, Fokusring und Links |
dark | boolean | false | Dunkle Palette |
sortOptions | array | Relevanz + created_at:desc | Sortier-Dropdown; der erste Eintrag muss default nutzen |
Fehler
{
"error": {
"code": "UNAUTHORIZED",
"message": "Send the API key as an Authorization header; embed sessions cannot be minted from a query parameter",
"request_id": "req_7b9d1f3a5c7e9b1d3f5a7c9e"
}
}| Status | Code | Wann |
|---|---|---|
| 401 | UNAUTHORIZED | Schlüssel fehlt, ist fehlerhaft, unbekannt oder widerrufen; Schlüssel als Query-Parameter gesendet |
| 403 | ORIGIN_NOT_ALLOWED | Browser-Origin außerhalb der allowedOrigins des Schlüssels |
| 404 | INDEX_NOT_FOUND | Für den Schlüssel ist kein Index verfügbar, oder index nennt einen Index, den der Schlüssel nicht nutzen kann |
| 422 | VALIDATION_ERROR | Ungültiger Body |
| 429 | RATE_LIMITED | Adressbezogenes Limit auf /embed/* (anonymer Embed-Traffic hat ein kleineres Budget) |
Endpunkte, die das Token akzeptieren
Hält die Seite ein Token, spricht das Widget nur noch mit diesen Endpunkten. Alle authentifizieren sich mit dem Token — im JSON-Body (token) oder, wo angegeben, als Authorization: Bearer aac_sess_… — nie mit einem API-Schlüssel. Jeder beantwortet außerdem einen CORS-Preflight (OPTIONS) mit 204.
| Endpunkt | Zweck |
|---|---|
POST /api/v1/embed/config | Einstellungen zu einem bereits ausgestellten Token: { "token": "…" } liefert index, scope, expires_at und settings |
POST /api/v1/embed/search | Such-Proxy. Body { token, index, client_id?, personalize?, searches: [...] } mit bis zu 5 Suchen; das Token geht auch als Bearer-Header. page ≤ 20, per_page wird auf 50 gekappt |
POST /api/v1/embed/events | Verhaltensereignisse: type ist view, click, conversion, add_to_cart oder impression (das Widget selbst sendet view, click und conversion), mit document_id, position, query, query_id, session_id, value_cents, items |
POST /api/v1/embed/recommendations | Verwandte und personalisierte Einträge für einen Besucher: client_id, seed_document_id, seed_query, limit (≤ 40) |
POST /api/v1/embed/widget-session | Für gespeicherte Widgets der Konsole: eine öffentliche Widget-ID (wgt_…) plus erlaubter Origin liefert eine Session — ganz ohne Schlüssel auf der Seite |
Die Suche über den Proxy durchläuft dieselbe Pipeline wie die öffentliche API (Query-Log, Regeln, Defaults, Relaxation, Did-you-mean); die Analytics sehen also den Widget-Traffic. Jede Embed-Suche zählt gegen das searches-Kontingent des Mandanten.
Token-Fehler an diesen Endpunkten:
| Status | Code | Bedeutung |
|---|---|---|
| 401 | EMBED_SESSION_REQUIRED | Es wurde kein Token gesendet |
| 401 | EMBED_SESSION_EXPIRED | Die Laufzeit des Tokens ist abgelaufen — neues ausstellen |
| 401 | EMBED_SESSION_INVALID | Fehlerhaftes oder manipuliertes Token |
| 403 | FORBIDDEN | Dem Token fehlt der Scope search:read |
| 403 | INDEX_MISMATCH | Die Anfrage nennt einen anderen Index als das Token |
Browserzugriff
Verwenden Sie POST /embed/session und /embed/search für das Widget oder eine eigene Browseroberfläche. Ihr Server tauscht den API-Schlüssel gegen ein kurzlebiges Token; der Browser erhält keine Infrastruktur-Zugangsdaten oder Speichernamen. Der erweiterte Endpunkt /scoped-key wird vom Widget nicht verwendet und ersetzt die Such-API der Plattform nicht.