AACSearch
API-Referenz

Dokumente

Ingest, Update, Löschen und Export von Dokumenten — POST /documents, Massen-Update, TTL, idempotente Wiederholungen und NDJSON-Export.

POST   /api/v1/indexes/{slug}/documents
POST   /api/v1/indexes/{slug}/documents/bulk
GET    /api/v1/indexes/{slug}/documents/{externalId}
DELETE /api/v1/indexes/{slug}/documents/{externalId}
DELETE /api/v1/indexes/{slug}/documents?filter_by=…
GET    /api/v1/indexes/{slug}/documents/export

Schreiben erfordert documents:write, das Lesen eines einzelnen Dokuments search:read, der Export documents:read.

Aufbau eines Dokuments

FeldPflichtHinweis
external_idja1–256 Zeichen; Ihr Primärschlüssel im Index
contentjaIndizierter Text; bis SEARCH_MAX_CONTENT_LENGTH (Standard 200.000 Zeichen)
titlenein≤ 1000 Zeichen; Teil des Standard-query_by
urlnein≤ 2048 Zeichen, gespeichert, nicht indiziert
source_typenein≤ 64 Zeichen; facettierbar, trennt api, webhook, connector
tagsneinBis 50 Strings mit je ≤ 64 Zeichen
metadataneinBeliebiges JSON, als String gespeichert, nicht durchsuchbar
expires_atneinISO-8601-Datum; danach wird das Dokument aus dem Index entfernt
eigene FelderneinJedes im Indexschema deklarierte Feld wird durchgereicht

Batch-Upsert

curl -sS -X POST "$AAC_API/indexes/documentation/documents" \
  -H "Authorization: Bearer $AAC_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-2026-10-23-01" \
  -d '{
    "documents": [
      { "external_id": "pricing", "title": "Preise", "content": "Pro kostet 29 USD im Monat.", "url": "https://example.com/pricing", "tags": ["billing"] },
      { "external_id": "limits", "title": "Limits", "content": "600 Requests pro Minute und Schlüssel.", "tags": ["api"], "expires_at": "2026-12-31T23:59:59Z" }
    ],
    "action": "upsert"
  }'
{
	"index": "documentation",
	"indexed": 2,
	"failed": [],
	"usage": {
		"plan": "pro",
		"period": "2026-10",
		"resetsAt": "2026-11-01T00:00:00.000Z",
		"searches": { "used": 4120, "limit": 500000 },
		"documents": { "used": 139, "limit": 50000 }
	},
	"request_id": "req_2a6c8e0b4d1f3a5c7e9b1d3f"
}

action bestimmt den Umgang mit vorhandenen Dokumenten:

AktionVerhalten
upsert (Standard)Vorhandenes Dokument ersetzen, neues einfügen
createNur einfügen; eine vorhandene external_id scheitert für dieses Dokument
updateTeil-Update — nur gesendete Felder ändern sich
emplaceEinfügen oder zusammenführen, ohne ausgelassene Felder zu leeren

Ein Batch ist nicht atomar. Abgelehnte Dokumente kommen in failed zurück, der Rest wird indiziert:

{
	"index": "documentation",
	"indexed": 1,
	"failed": [{ "external_id": "draft-1", "error": "Document content is required" }],
	"usage": { "documents": { "used": 140, "limit": 50000 } },
	"request_id": "req_7d1e3b5a9c0f2a4d6b8e0c2a"
}

Limits: maximal 500 Dokumente pro Request und 200.000 Zeichen pro content. Wird eines überschritten, folgt 422 INVALID_DOCUMENT mit Details:

{
	"error": {
		"code": "INVALID_DOCUMENT",
		"message": "Too many documents in a single request",
		"request_id": "req_4c8a0e2b6d1f3a5c7b9d1e3f",
		"details": { "max": 500, "received": 812 }
	}
}

Idempotente Wiederholungen

Mit Idempotency-Key auf POST /documents liefert eine Wiederholung desselben Schreibvorgangs die ursprüngliche Antwort, statt Dokumente doppelt zu zählen:

curl -sS -i -X POST "$AAC_API/indexes/documentation/documents" \
  -H "Authorization: Bearer $AAC_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-2026-10-23-01" \
  -d '{"documents":[{"external_id":"pricing","content":"Pro kostet 29 USD im Monat."}]}'
HTTP/1.1 200 OK
idempotent-replayed: true

Replay-Einträge leben 10 Minuten: in Redis (instanzübergreifend) oder im Prozessspeicher, wenn Redis nicht konfiguriert ist.

So wird ein Schlüssel zugeordnet:

  • Schlüssel gelten pro Index: derselbe Schlüssel auf zwei Indizes sind zwei getrennte Schreibvorgänge.
  • Der Eintrag speichert einen Fingerabdruck aus Methode, Pfad und JSON-Body. Wird ein Schlüssel für einen anderen Body wiederverwendet, antwortet die API mit 422 IDEMPOTENCY_KEY_REUSED, statt die alte Antwort zu wiederholen und den neuen Schreibvorgang still zu verwerfen.
  • Solange der erste Request noch läuft, erhält ein paralleles Duplikat 409 IDEMPOTENCY_IN_FLIGHT; kurz warten und erneut senden. Die In-Flight-Markierung läuft nach 120 Sekunden ab.
  • Ein fehlgeschlagener Schreibvorgang wird nicht gespeichert; derselbe Schlüssel kann sofort erneut verwendet werden.
  • Nur eine erfolgreiche Antwort (200) wird wiederholt, byte-genau einschließlich der ursprünglichen request_id.

Lesen, Ändern, Löschen

curl -sS "$AAC_API/indexes/documentation/documents/pricing" \
  -H "Authorization: Bearer $AAC_KEY"
{
	"document": {
		"id": "pricing",
		"external_id": "pricing",
		"title": "Preise",
		"content": "Pro kostet 29 USD im Monat.",
		"url": "https://example.com/pricing",
		"source_type": "api",
		"organization_id": "org_01h9",
		"tags": ["billing"],
		"indexed_at": 1761214364
	},
	"request_id": "req_9e2b4d6a8c0f1e3b5d7a9c1e"
}
curl -sS -X DELETE "$AAC_API/indexes/documentation/documents/pricing" \
  -H "Authorization: Bearer $AAC_KEY"
{
	"deleted": true,
	"external_id": "pricing",
	"request_id": "req_1a3c5e7b9d0f2a4c6e8b0d1f"
}

Ein fehlendes Dokument ist 404 DOCUMENT_NOT_FOUND:

{
	"error": {
		"code": "DOCUMENT_NOT_FOUND",
		"message": "Document \"pricing\" was not found",
		"request_id": "req_6b0d2f4a8c1e3b5d7f9a1c3e"
	}
}

Massen-Update und Löschen per Filter

curl -sS -X POST "$AAC_API/indexes/documentation/documents/bulk" \
  -H "Authorization: Bearer $AAC_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filter_by": "tags:=[billing]", "document": { "category": "pricing" } }'
{
	"index": "documentation",
	"updated": 12,
	"filter_by": "tags:=[billing]",
	"request_id": "req_3d5f7b9a1c2e4a6b8d0f2a4c"
}
curl -sS -X DELETE "$AAC_API/indexes/documentation/documents" \
  -H "Authorization: Bearer $AAC_KEY" \
  -G --data-urlencode "filter_by=source_type:=connector && indexed_at:<1761214364"
{
	"index": "documentation",
	"deleted": 348,
	"filter_by": "source_type:=connector && indexed_at:<1761214364",
	"request_id": "req_8f0b2d4a6c8e1b3d5f7a9c1e"
}

Für das filterbasierte Löschen ist filter_by Pflicht; ohne den Parameter kommt 400 INVALID_QUERY mit der Meldung filter_by query parameter is required. Der Mandantenfilter wird wie bei der Suche injiziert.

Export

curl -sS "$AAC_API/indexes/documentation/documents/export" \
  -H "Authorization: Bearer $AAC_KEY" \
  -o documentation.ndjson

Die Antwort ist application/x-ndjson mit content-disposition: attachment; filename="documentation-export.ndjson", ein JSON-Dokument pro Zeile — genau das Format, das der Import akzeptiert, ein Export lässt sich also in einen anderen Index zurückspielen.

Ablaufende Dokumente

expires_at wird mit dem Dokument gespeichert und vom Retention-Job durchgesetzt, der abgelaufene Dokumente aus der Collection entfernt. Das ist der günstigste Weg, zeitlich begrenzte Inhalte (Events, Releases, Aktionen) aus den Ergebnissen zu halten, ohne einen Delete-Aufruf. Der Lauf gehört zum Scheduler; mit POST /admin/jobs/run?job=retention stoßen Sie ihn selbst an.

Schreiben per Webhook

Kommen Dokumente aus einem anderen System, nimmt POST /indexes/{slug}/webhook dasselbe Dokument-Array mit HMAC-Signatur entgegen und erlaubt bis zu 1000 Dokumente pro Aufruf. Siehe Webhooks.

Feed vor dem Import prüfen

POST /indexes/{indexSlug}/feed-preview (Scope documents:write) liest einen XML-, YAML-, CSV- oder JSON-Feed von einer öffentlichen URL und liefert erkannte Felder, ein vorgeschlagenes Mapping, ein gemapptes Beispiel und einen Schema-Vorschlag. Es wird nichts in den Index geschrieben. Mit mapping ({ "rules": [{ "source", "target", "transforms" }] }) lassen sich eigene Regeln testen. Transformationen: strip_html, to_number, to_bool, split, join, default, lowercase, date_to_unix, first_non_empty. Ein nicht lesbarer Feed ergibt 422 FEED_UNREADABLE.

On this page