> For the complete documentation index, see [llms.txt](https://docs.codegiganten.de/plugin-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.codegiganten.de/plugin-documentation/api-dokumentationen/insightsearch-suche/uberarbeitette-doku-v2.md).

# Überarbeitette Doku v2

**Content-Type:** `application/json`

***

## 1. Übersicht & Authentifizierung

### API-Endpunkte

InsightSearch betreibt zwei separate Services:

| Service           | Base URL                              | Verwendung                                     |
| ----------------- | ------------------------------------- | ---------------------------------------------- |
| **API**           | `https://api.insightsearch.de/api`    | Feed-Verwaltung, Konfiguration, Tracking, Auth |
| **Search Engine** | `https://search.insightsearch.de/api` | Suche, Suggest (Autocomplete)                  |

### Token-Typen

| Token               | Beschreibung                                                                                                                                 | Format                     |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `accountSecret`     | Identifiziert den Account (wird einmalig von InsightSearch bereitgestellt, beginnt mit `A`)                                                  | String, min. 10 Zeichen    |
| `installationToken` | Identifiziert die Plugin-Installation (wird einmalig von InsightSearch bereitgestellt, beginnt mit `I`)                                      | String, min. 10 Zeichen    |
| `sessionToken`      | Kurzlebiger Token für Frontend-Anfragen (Suche, Suggest, Empfehlungen). Wird serverseitig aus `accountSecret` + `installationToken` erzeugt. | Hex-String, 40–128 Zeichen |

> **Wichtig:** `accountSecret` und `installationToken` werden vom Cogi-Team bereitgestellt und sollten **nie im Frontend exponiert** werden. Nur der `sessionToken` wird an den Browser weitergegeben.

> **Backend-Requests (Feed & Push):** Im Request-Body heißen die Felder `installation_secret` und `account_secret` (snake\_case) und es wird **zusätzlich** das Feld `shop_type` benötigt (für Shopware: `Shopware`). Praktische Anleitung inkl. Postman: siehe [16. Postman & Fehlersuche](#16-postman--fehlersuche).

### Kernkonzept: Feed, Index & Index-ID

Ein **Feed** ist die Produktdatenquelle eines Shops. Beim Registrieren eines Feeds erstellt InsightSearch daraus einen **Index**: den durchsuchbaren Produktdatenbestand.

**Pull oder Push — zwei Wege, die Produktdaten zu liefern:**

|                | **Pull (Feed-URL)**                                                                                         | **Push (API)**                                                                       |
| -------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Prinzip        | InsightSearch ruft die Feed-URL des Shops selbst ab                                                         | Der Shop überträgt seine Produkte aktiv in Batches per API                           |
| Registrierung  | [`feed/register`](#61-feed-registrieren)                                                                    | [`push/register`](#71-push-index-registrieren)                                       |
| Voraussetzung  | Öffentlich erreichbare XML/CSV-Feed-URL (Format siehe [6.5](#65-feed-format-beispiel-xml))                  | Keine — der Shop muss nicht von außen erreichbar sein                                |
| Aktualisierung | Automatisch 1× täglich (Uhrzeit konfigurierbar) sowie manuell per [`feed/reindex`](#63-feed-neu-indexieren) | Der Shop bestimmt Zeitpunkt und Häufigkeit selbst (Zyklus `start → batch → finish`)  |
| Geeignet für   | Einfachste Integration, wenn der Shop bereits einen Produkt-Feed bereitstellt                               | Volle Kontrolle über den Indexierungszeitpunkt, nicht öffentlich erreichbare Systeme |

> Der Modus wird **bei der Registrierung festgelegt** und gilt dauerhaft für den Index: Ein Pull-Index kann nicht per Push befüllt werden und umgekehrt — `feed/reindex` auf einem Push-Index antwortet mit `409 push_index_requires_push_reindex`. Für Suche, Suggest, Konfiguration und Tracking verhalten sich beide Varianten identisch.

Die Register-Response enthält das Feld `index.id` — **diese ID ist die zentrale Kennung des Index** und wird in allen weiteren Endpunkten benötigt. Historisch bedingt heißt der Parameter je nach Endpunkt-Gruppe unterschiedlich:

| Endpunkt-Gruppe                             | Parametername   |
| ------------------------------------------- | --------------- |
| Suche / Suggest                             | `searchIndexId` |
| Empfehlungen                                | `searchIndexId` |
| Feed-Verwaltung (reindex, delete)           | `index_id`      |
| Konfiguration (Synonyme, Filter, Attribute) | `indicesId`     |
| Tracking / Statistiken                      | `searchIndexId` |

> **Alle diese Parameter bezeichnen dieselbe ID** — die `index.id` aus der Feed- bzw. Push-Registrierung. Die ID sollte nach der Registrierung dauerhaft gespeichert werden (z.B. in der Plugin-Konfiguration des Shops).

Ein Account kann mehrere Indizes besitzen (z.B. pro Sprache/Verkaufskanal je ein Index. z.b hat ein Shop 5 Sprachen, so wird für jede Sprache ein Index bereitgestellt. Jenachdem welche Sprache der Kunde in der Storefront nutzt, wird der entsprechende Index in den API Request für die Suche verwendet).

### Integrations-Ablauf (Quick Start)

{% stepper %}
{% step %}

## Feed registrieren (einmalig)

`POST /api/plugin/feed/register` → Response: `index.id` ◄── ID speichern!

(Alternativ Push: `POST /api/plugin/push/register`)
{% endstep %}

{% step %}

## Daten indexieren

**Feed-Variante:** Indexierung startet automatisch.\
Status prüfen: `POST /api/plugin/feed/validate`\
bis `message = "feed_index_success"`

**Push-Variante:** `POST /api/plugin/push/start` → `batch` (n-mal) → `finish`
{% endstep %}

{% step %}

## Session-Token erzeugen (serverseitig, \~1x pro Stunde)

`POST /api/plugin/auth/session-token` → Response: `token`
{% endstep %}

{% step %}

## Suchen / Suggest (Frontend)

`POST https://search.insightsearch.de/api/search`

Body: `{ sessionToken, searchIndexId: <index.id>, query, locale }`
{% endstep %}

{% step %}

## Optional

* **Konfiguration:** Synonyme / Filter / Attribute (`indicesId = index.id`)
* **Tracking:** `user-event` / `order` / `cart` (`searchIndexId = index.id`)
* **Re-Indexierung:** `feed/reindex` bzw. neuer Push-Zyklus (`index.id` bleibt gleich; Pull-Feeds werden zusätzlich automatisch 1× täglich aktualisiert)
  {% endstep %}
  {% endstepper %}

Wichtig dabei:

* **Schritt 1 ist einmalig** — die `index.id` bleibt über die gesamte Lebensdauer des Feeds stabil, auch bei Re-Indexierungen.
* **Suchanfragen sind erst nach erfolgreicher Indexierung möglich.** Vorher antwortet die Suche mit `409 ERR_INDEX_MISSING_KEY`.
* Suche/Suggest laufen gegen `search.insightsearch.de`, alle übrigen Endpunkte gegen `api.insightsearch.de`.

### Caching

InsightSearch cacht auf mehreren Ebenen. Relevante Auswirkungen für die Integration:

| Cache                     | Dauer (Standard) | Auswirkung                                                                                                                                                                                    |
| ------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Suchergebnisse            | \~60 Sekunden    | Identische Anfragen (Query + Seite + Sortierung + `searchMode` + Filter) liefern serverseitig gecachte Antworten. Das Feld `cache_unix` im Response zeigt den Erzeugungszeitpunkt der Antwort |
| Suggest-Ergebnisse        | max. 60 Sekunden | Wie Suchergebnisse                                                                                                                                                                            |
| Index-Konfiguration       | bis \~5 Minuten  | Änderungen an Filtern, Attributen, Gewichtungen oder Synonymen werden von der Suche erst nach Ablauf dieses Caches berücksichtigt                                                             |
| Session-Token-Validierung | bis \~5 Minuten  | Token-Prüfungen werden serverseitig gecacht                                                                                                                                                   |

* Nach einer **Re-Indexierung** (Feed oder Push) ist der neue Datenbestand sofort live; einzelne Suchantworten können wegen des Ergebnis-Caches aber noch bis zu \~60 Sekunden den alten Stand zeigen.
* Der `sessionToken` selbst ist **1 Stunde** gültig und sollte im Shop-Backend gecacht werden — nicht pro Suchanfrage neu erzeugen (siehe [Abschnitt 2](#2-session-token)).

***

## 2. Session Token

### Ablauf

Der `sessionToken` ist das zentrale Auth-Element für alle Frontend-Anfragen (Suche, Suggest, Empfehlungen). Der typische Ablauf:

```
Shop-Backend (serverseitig)
    │
    ├─► POST /api/plugin/auth/session-token
    │       Body: { accountSecret, installationToken }
    │       ◄── Response: { token, expiresAt, ttlSeconds }
    │
    └─► token an Frontend übergeben (z.B. als JS-Variable)
             │
             ├─► POST https://search.insightsearch.de/api/search
             │       Body: { sessionToken, searchIndexId, query, ... }
             │
             └─► POST https://search.insightsearch.de/api/suggest
                     Body: { sessionToken, searchIndexId, query, ... }
```

* Der Token ist **1 Stunde** gültig (`ttlSeconds: 3600`).
* Der Token sollte **serverseitig gecacht** werden und erst kurz vor Ablauf neu erzeugt werden.
* Ein abgelaufener oder ungültiger `sessionToken` resultiert in `401 ERR_SESSION_TOKEN_INVALID`.

### Session Token erzeugen

```
POST https://api.insightsearch.de/api/plugin/auth/session-token
```

**Request Body:**

| Parameter           | Typ    | Required | Beschreibung                          |
| ------------------- | ------ | -------- | ------------------------------------- |
| `accountSecret`     | string | ✅        | Account-Secret (min. 10 Zeichen)      |
| `installationToken` | string | ✅        | Installations-Token (min. 10 Zeichen) |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationToken": "I1b2c3d4e5f6g7h8i9j0"
}
```

**Response (200):**

```json
{
  "token": "a3f8e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4",
  "expiresAt": "2026-04-14T10:08:00+00:00",
  "ttlSeconds": 3600
}
```

| Feld         | Beschreibung                                                         |
| ------------ | -------------------------------------------------------------------- |
| `token`      | Der Session-Token, der für Suche/Suggest/Empfehlungen verwendet wird |
| `expiresAt`  | Ablaufzeitpunkt (ISO 8601)                                           |
| `ttlSeconds` | Gültigkeitsdauer in Sekunden (Standard: 3600)                        |

***

## 3. Suche

```
POST https://search.insightsearch.de/api/search
```

Führt eine Volltextsuche mit Natural-Language-Parsing, automatischer Filter-Erkennung, Facettierung und Paginierung durch.

### Request Body

| Parameter       | Typ             | Required | Beschreibung                                                                                                                |
| --------------- | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `sessionToken`  | string          | ✅        | Gültiger Session-Token (40–128 Hex-Zeichen)                                                                                 |
| `searchIndexId` | integer         | ✅        | Die `index.id` aus der Feed-/Push-Registrierung (siehe [Kernkonzept](#kernkonzept-feed-index--index-id)). Alias: `engineId` |
| `query`         | string          | ✅        | Suchbegriff (max. 512 Zeichen). Unterstützt Natural-Language-Queries, z.B. `"jeans unter 50€"`                              |
| `locale`        | string          | ✅        | Sprachcode des Shops, z.B. `de-DE` oder `en-US`                                                                             |
| `page`          | integer         | ❌        | Seite (Default: `1`, min. `1`)                                                                                              |
| `perPage`       | integer         | ❌        | Ergebnisse pro Seite (Default: `24`, min. `1`, max. `60`)                                                                   |
| `sort`          | string          | ❌        | Sortierung (Default: `relevance`)                                                                                           |
| `searchMode`    | string          | ❌        | Such-Modus: `standard`, `precise` oder `explorative` (Default: `standard`, siehe unten)                                     |
| `filter`        | object \| array | ❌        | Filter-Objekt oder Filter-Liste (siehe unten). Alias: `filters`. Kann auch als JSON-String übergeben werden                 |
| `isoCode`       | string          | ❌        | ISO-Währungscode für Statistiken, z.B. `EUR`                                                                                |

**Sortier-Optionen (`sort`):**

| Wert         | Beschreibung             |
| ------------ | ------------------------ |
| `relevance`  | Nach Relevanz (Standard) |
| `price_asc`  | Preis aufsteigend        |
| `price_desc` | Preis absteigend         |
| `newest`     | Neueste zuerst           |
| `oldest`     | Älteste zuerst           |
| `title_asc`  | Titel A–Z                |
| `title_desc` | Titel Z–A                |

**Such-Modi (`searchMode`):**

Der Such-Modus steuert die Suchstrategie pro Anfrage. Requests ohne `searchMode` verhalten sich exakt wie bisher (`standard`) — der Parameter ist vollständig abwärtskompatibel.

| Wert          | Beschreibung                                                                                                                                                                                                  |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `standard`    | Ausgewogene Suche (Default): Tippfehler-Toleranz (Fuzzy), Teilwort-/Präfix-Treffer und automatische Artikelnummern-Erkennung                                                                                  |
| `precise`     | Exakte Suche: keine Tippfehler-Toleranz, alle Suchbegriffe müssen vorkommen (UND-Verknüpfung). Artikelnummern/SKUs treffen ausschließlich Code-Felder. Empfohlen für Shops mit artikelnummern-lastigen Suchen |
| `explorative` | Maximale Trefferbreite: aggressive Tippfehler-Toleranz und zusätzliche Teilwort-Indizes (N-Gramme). Empfohlen für Discovery-orientierte Sortimente                                                            |

> **Hinweis:** `searchMode` gilt nur für `/api/search`. Suggest (`/api/suggest`) wird davon nicht beeinflusst.

**Filter-Objekt (`filter`) — Format A: Objekt:**

```json
{
  "categories": ["hosen", "jeans"],
  "brands": ["Levi's", "Wrangler"],
  "attrs": {
    "color": ["blau", "schwarz"],
    "size": ["M", "L"]
  },
  "price": {
    "min": 10.00,
    "max": 50.00
  }
}
```

**Filter-Objekt (`filter`) — Format B: Liste:**

```json
[
  { "key": "categories", "value": ["hosen"] },
  { "key": "brands", "value": "Levi's" },
  { "key": "price", "value": { "min": 10, "max": 50 } }
]
```

### Beispiel Request

```json
{
  "sessionToken": "a3f8e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4",
  "searchIndexId": 5,
  "query": "slim fit jeans unter 50",
  "locale": "de-DE",
  "page": 1,
  "perPage": 24,
  "sort": "relevance",
  "searchMode": "standard",
  "filter": {
    "brands": ["Levi's"],
    "price": { "max": 50 }
  }
}
```

### Response (200)

```json
{
  "q": "slim fit jeans",
  "took_ms": 14,
  "products": [
    {
      "id": "SW10001",
      "title": "Slim Fit Jeans",
      "url": "https://shop.example.com/jeans/slim-fit",
      "image": "https://cdn.example.com/jeans-slim.jpg",
      "price": 49.99,
      "listPrice": 69.99,
      "currency": "EUR",
      "brand": "Levi's",
      "badge": [
        { "title": "Sale", "color": "#e53e3e" }
      ]
    }
  ],
  "filters": {
    "config": {
      "category": { "key": "categories", "label": "Kategorie" },
      "terms": [...],
      "attributes": [...],
      "ranges": [...],
      "byId": {}
    },
    "groups": [
      {
        "id": "brands",
        "label": "Marke",
        "type": "terms",
        "options": [
          { "value": "Levi's", "count": 12, "active": true }
        ]
      }
    ],
    "applied": {
      "brands": ["Levi's"],
      "price": { "max": 50 }
    },
    "appliedById": {},
    "filtered": {},
    "all": {}
  },
  "suggest": {
    "ac": [],
    "dym": null
  },
  "pagination": {
    "page": 1,
    "perPage": 24,
    "from": 0,
    "size": 24,
    "total": 47,
    "totalPages": 2,
    "lastPage": false,
    "hasNext": true,
    "hasPrev": false
  },
  "cache_unix": 1781424000
}
```

**Response-Felder:**

| Feld                        | Beschreibung                                                                                                                                                                |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `q`                         | Bereinigter Suchbegriff nach NLP-Verarbeitung                                                                                                                               |
| `took_ms`                   | Verarbeitungszeit in Millisekunden (OpenSearch)                                                                                                                             |
| `products`                  | Array der Treffer (alle Feed-Felder werden durchgereicht)                                                                                                                   |
| `products[].id`             | Produkt-ID                                                                                                                                                                  |
| `products[].title`          | Produkttitel                                                                                                                                                                |
| `products[].url`            | Produkt-URL                                                                                                                                                                 |
| `products[].image`          | Bild-URL                                                                                                                                                                    |
| `products[].price`          | Verkaufspreis (bei Sale: Sale-Preis; sonst regulärer Preis)                                                                                                                 |
| `products[].listPrice`      | Regulärer Preis / UVP (bei Sale: regulärer Preis; sonst ggf. `list_price`)                                                                                                  |
| `products[].referencePrice` | Referenzpreis (Grundpreis)                                                                                                                                                  |
| `products[].currency`       | Währungscode                                                                                                                                                                |
| `products[].brand`          | Marke                                                                                                                                                                       |
| `products[].badge`          | Badges (Array mit `title` und `color`), `null` wenn keine vorhanden                                                                                                         |
| `products[].unit`           | Einheit                                                                                                                                                                     |
| `products[].purchaseUnit`   | Kaufmenge                                                                                                                                                                   |
| `products[].referenceUnit`  | Referenzmenge                                                                                                                                                               |
| `filters.config`            | Filter-Konfiguration des Index (Kategorien, Terms, Attribute, Ranges)                                                                                                       |
| `filters.groups`            | Aufbereitete Filter-Gruppen für das Rendering                                                                                                                               |
| `filters.applied`           | Aktuell angewandte Filter                                                                                                                                                   |
| `filters.appliedById`       | Angewandte Filter, indiziert nach ID                                                                                                                                        |
| `filters.filtered`          | Filter-Optionen mit aktiven Filtern                                                                                                                                         |
| `filters.all`               | Filter-Optionen ohne Filter (alle verfügbaren Werte)                                                                                                                        |
| `suggest`                   | "Meinten Sie?"-Vorschlag (aus OpenSearch)                                                                                                                                   |
| `pagination`                | Paginierungsinformationen                                                                                                                                                   |
| `pagination.total`          | Gesamttreffer                                                                                                                                                               |
| `pagination.totalPages`     | Gesamtseiten                                                                                                                                                                |
| `pagination.hasNext`        | Weitere Seiten verfügbar                                                                                                                                                    |
| `pagination.hasPrev`        | Vorherige Seiten verfügbar                                                                                                                                                  |
| `cache_unix`                | Unix-Timestamp der Antwort-Erzeugung. Antworten werden serverseitig gecacht — hieran lässt sich das Alter eines Cache-Treffers erkennen                                     |
| `broken_filter_fields`      | Nur vorhanden, wenn einzelne Filter-Felder bei der Facettierung fehlschlagen: Liste der betroffenen Feldnamen. Diese Filter werden für die Anfrage automatisch übersprungen |

### Fehlercodes

| HTTP | Code                        | Beschreibung                                           |
| ---- | --------------------------- | ------------------------------------------------------ |
| 401  | `ERR_SESSION_TOKEN_INVALID` | Session-Token fehlt, ungültig oder abgelaufen          |
| 404  | `ERR_INDEX_NOT_FOUND`       | Index-ID existiert nicht oder gehört nicht zum Account |
| 409  | `ERR_INDEX_MISSING_KEY`     | Index noch nicht vollständig initialisiert             |
| 502  | `ERR_SEARCH_BACKEND`        | Interner Fehler                                        |

***

## 4. Suggest / Autocomplete

```
POST https://search.insightsearch.de/api/suggest
```

Liefert Suchvorschläge in Echtzeit während der Eingabe (Autocomplete / Search-as-you-type). Kombiniert Prefix-Matching, Popularity-Scoring und optionale Fuzzy-Suche.

### Request Body

| Parameter       | Typ     | Required | Beschreibung                                                                                                                |
| --------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `sessionToken`  | string  | ✅        | Gültiger Session-Token (40–128 Hex-Zeichen)                                                                                 |
| `searchIndexId` | integer | ✅        | Die `index.id` aus der Feed-/Push-Registrierung (siehe [Kernkonzept](#kernkonzept-feed-index--index-id)). Alias: `engineId` |
| `query`         | string  | ✅        | Suchbegriff (max. 512 Zeichen)                                                                                              |
| `locale`        | string  | ❌        | Sprachcode, z.B. `de-DE` (empfohlen)                                                                                        |
| `isoCode`       | string  | ❌        | ISO-Währungscode, z.B. `EUR`                                                                                                |
| `limit`         | integer | ❌        | Anzahl Vorschläge (Default: `5`, min. `1`, max. `5`)                                                                        |

### Beispiel Request

```json
{
  "sessionToken": "a3f8e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4",
  "searchIndexId": 5,
  "query": "slim",
  "locale": "de-DE",
  "limit": 5
}
```

### Response (200)

```json
{
  "suggestions": [
    {
      "text": "slim fit jeans",
      "type": "query",
      "score": 142.5,
      "meta": {
        "source_type": "query",
        "highlighted": "slim fit jeans",
        "target": {
          "type": "search",
          "query": "slim fit jeans",
          "filter": {}
        }
      }
    },
    {
      "text": "slim fit chino",
      "type": "query",
      "score": 98.3,
      "meta": {
        "source_type": "query",
        "highlighted": "slim fit chino",
        "target": {
          "type": "search",
          "query": "slim fit chino",
          "filter": {}
        }
      }
    }
  ],
  "dym": {
    "text": null,
    "active": false
  }
}
```

**Response-Felder:**

| Feld                             | Beschreibung                                                            |
| -------------------------------- | ----------------------------------------------------------------------- |
| `suggestions`                    | Array der Vorschläge (max. `limit` Einträge)                            |
| `suggestions[].text`             | Anzeigetext des Vorschlags                                              |
| `suggestions[].type`             | Quelle: `query`, `brand`, `category`, `attribute`, `product`, `synonym` |
| `suggestions[].score`            | Interner Relevanz-Score                                                 |
| `suggestions[].meta.source_type` | Quelle des Vorschlags                                                   |
| `suggestions[].meta.highlighted` | Text mit Hervorhebung des Eingabe-Matches                               |
| `suggestions[].meta.target`      | Zielaktion: `type` (`search` oder `category`), `query`, `filter`        |
| `dym.text`                       | "Did you mean?"-Text, `null` wenn nicht vorhanden                       |
| `dym.active`                     | `true` wenn ein DYM-Vorschlag aktiv ist                                 |

### Fehlercodes

| HTTP | Code                        | Beschreibung                                  |
| ---- | --------------------------- | --------------------------------------------- |
| 401  | `ERR_SESSION_TOKEN_INVALID` | Session-Token fehlt, ungültig oder abgelaufen |
| 404  | `ERR_INDEX_NOT_FOUND`       | Index nicht gefunden                          |
| 409  | `ERR_INDEX_MISSING_KEY`     | Index noch nicht initialisiert                |

***

## 5. Empfehlungen

DOKU folgt.

***

## 6. Feed-Verwaltung

Die Feed-Verwaltung ist die **Pull-Variante** der Indexierung (siehe [Pull vs. Push](#kernkonzept-feed-index--index-id)): Der Shop stellt eine öffentlich erreichbare XML/CSV-Feed-URL bereit, InsightSearch ruft diese ab — **automatisch einmal täglich** (Uhrzeit konfigurierbar) sowie auf Anforderung per [`reindex`](#63-feed-neu-indexieren). Die Registrierung liefert die `index.id`, mit der der Index anschließend in Suche, Suggest, Konfiguration und Tracking referenziert wird (siehe [Kernkonzept](#kernkonzept-feed-index--index-id) und [Integrations-Ablauf](#integrations-ablauf-quick-start)).

Alle Feed-Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/feed/...` und erfordern `account_secret` + `installation_secret`.

### 6.1 Feed registrieren

Erstellt einen neuen Produkt-Feed und startet die erste Indexierung.

```
POST https://api.insightsearch.de/api/plugin/feed/register
```

**Request Body:**

| Parameter             | Typ          | Required | Beschreibung                                                   |
| --------------------- | ------------ | -------- | -------------------------------------------------------------- |
| `installation_secret` | string       | ✅        | Installations-Token                                            |
| `account_secret`      | string       | ✅        | Account-Secret                                                 |
| `shop_type`           | string       | ✅        | Shop-Typ (z.B. `Shopware`, max. 100 Zeichen)                   |
| `name`                | string       | ✅        | Name des Feeds (max. 255 Zeichen)                              |
| `feed_url`            | string (URL) | ✅        | URL zum Produkt-Feed (XML/CSV)                                 |
| `language_code`       | string       | ✅        | Sprachcode(s), kommagetrennt (z.B. `de-DE` oder `de-DE,en-US`) |
| `currency`            | string       | ✅        | Währungscode (max. 8 Zeichen, z.B. `EUR`)                      |
| `index_type`          | string       | ❌        | `SEARCH` oder `RECOMMENDATIONS` (Default: `SEARCH`)            |

**Beispiel Request:**

```json
{
  "installation_secret": "I1b2c3d4e5f6g7h8i9j0",
  "account_secret": "A1b2c3d4e5f6g7h8i9j0",
  "shop_type": "Shopware",
  "name": "Mein Shop DE",
  "feed_url": "https://shop.example.com/export/insightsearch.xml",
  "language_code": "de-DE",
  "currency": "EUR"
}
```

**Response (201):**

```json
{
  "success": true,
  "index": {
    "id": 5,
    "name": "Mein Shop DE",
    "feed_url": "https://shop.example.com/export/insightsearch.xml",
    "language": "de-DE",
    "currency": "EUR",
    "state": "running"
  }
}
```

> **Wichtig:** `index.id` ist die zentrale Index-ID für alle weiteren Endpunkte — sie wird als `searchIndexId` (Suche, Suggest, Tracking), `indicesId` (Konfiguration) bzw. `index_id` (Feed-Verwaltung) verwendet und sollte dauerhaft gespeichert werden. Siehe [Kernkonzept](#kernkonzept-feed-index--index-id).

***

### 6.2 Feed-Status prüfen

Prüft den Indexierungs-Status eines Feeds.

```
POST https://api.insightsearch.de/api/plugin/feed/validate
```

**Request Body:**

| Parameter             | Typ          | Required | Beschreibung                                              |
| --------------------- | ------------ | -------- | --------------------------------------------------------- |
| `installation_secret` | string       | ✅        | Installations-Token                                       |
| `account_secret`      | string       | ✅        | Account-Secret                                            |
| `shop_type`           | string       | ✅        | Shop-Typ                                                  |
| `feed_url`            | string (URL) | ❌        | Feed-URL (wenn nicht angegeben: erster Feed des Accounts) |

**Response (200):**

```json
{
  "success": true,
  "message": "feed_index_success",
  "data": {
    "created": true,
    "validate": true,
    "quality": true,
    "indexed": true
  }
}
```

**Mögliche `message`-Werte:**

| Message                    | Beschreibung                                          |
| -------------------------- | ----------------------------------------------------- |
| `feed_not_found`           | Kein Feed gefunden                                    |
| `feed_index_pending`       | Indexierung läuft                                     |
| `feed_index_error`         | Indexierung fehlgeschlagen                            |
| `feed_index_retry_started` | Automatischer Retry gestartet (nach Fehler > 15 Min.) |
| `feed_index_success`       | Indexierung erfolgreich abgeschlossen                 |

***

### 6.3 Feed neu indexieren

Startet die Neuindexierung eines bestehenden Feeds.

```
POST https://api.insightsearch.de/api/plugin/feed/reindex
```

**Request Body:**

| Parameter             | Typ     | Required | Beschreibung          |
| --------------------- | ------- | -------- | --------------------- |
| `installation_secret` | string  | ✅        | Installations-Token   |
| `account_secret`      | string  | ✅        | Account-Secret        |
| `shop_type`           | string  | ✅        | Shop-Typ              |
| `index_id`            | integer | ✅        | ID des Index (min. 1) |

**Beispiel Request:**

```json
{
  "installation_secret": "I1b2c3d4e5f6g7h8i9j0",
  "account_secret": "A1b2c3d4e5f6g7h8i9j0",
  "shop_type": "Shopware",
  "index_id": 5
}
```

**Response (200):**

```json
{
  "success": true,
  "index": {
    "id": 5,
    "state": "running"
  }
}
```

**Fehlercodes:**

| HTTP | Beschreibung                                                                                                                                     |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| 404  | Installation, Account oder Index nicht gefunden                                                                                                  |
| 409  | Indexierung läuft bereits                                                                                                                        |
| 409  | Index ist ein Push-Index (`push_index_requires_push_reindex`) — Aktualisierung erfolgt über den Push-Zyklus ([Abschnitt 7](#7-push-indexierung)) |

***

### 6.4 Feed löschen

Löscht einen Feed inkl. aller zugehörigen Daten (OpenSearch-Indizes, Tracking, Attribute, Synonyme, Filter).

```
POST https://api.insightsearch.de/api/plugin/feed/delete
```

**Request Body:**

| Parameter             | Typ     | Required | Beschreibung          |
| --------------------- | ------- | -------- | --------------------- |
| `installation_secret` | string  | ✅        | Installations-Token   |
| `account_secret`      | string  | ✅        | Account-Secret        |
| `shop_type`           | string  | ✅        | Shop-Typ              |
| `index_id`            | integer | ✅        | ID des Index (min. 1) |

**Response (200):**

```json
{
  "success": true,
  "status": "deleted",
  "index": {
    "id": 5
  }
}
```

***

### 6.5 Feed-Format: Beispiel (XML)

InsightSearch liest Produkt-Feeds im **RSS-2.0-Format mit Google-Merchant-Namespace** (`xmlns:g="http://base.google.com/ns/1.0"`). Jedes Produkt ist ein `<item>`-Element (Atom-Feeds mit `<entry>` werden ebenfalls unterstützt).

```xml
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
  <channel>
    <title>Mein Shop DE</title>
    <link>https://shop.example.com</link>
    <language>de</language>

    <item>
      <g:id>SW10001</g:id>
      <title>Slim Fit Jeans</title>
      <link>https://shop.example.com/jeans/slim-fit</link>
      <g:image_link>https://cdn.example.com/jeans-slim.jpg</g:image_link>
      <g:condition>new</g:condition>
      <g:availability>in stock</g:availability>
      <g:price>49.99 EUR</g:price>
      <g:sale_price>39.99 EUR</g:sale_price>
      <g:brand>Levi's</g:brand>
      <g:gtin>4006381333931</g:gtin>
      <g:mpn>SW10001</g:mpn>
      <g:product_type>Bekleidung &gt; Hosen &gt; Jeans</g:product_type>
      <g:size>32/32</g:size>
      <g:color>blau</g:color>
      <g:material>Denim</g:material>
    </item>

    <item>
      <g:id>SW10005.1</g:id>
      <title>Basic Shirt (Variante M)</title>
      <link>https://shop.example.com/shirts/basic-m</link>
      <g:image_link>https://cdn.example.com/shirt.jpg</g:image_link>
      <g:condition>new</g:condition>
      <g:availability>in stock</g:availability>
      <g:price>19.99 EUR</g:price>
      <g:brand>Shopware Fashion</g:brand>
      <g:gtin/>
      <g:mpn>SWDEMO10005.1</g:mpn>
      <g:product_type>Bekleidung &gt; Shirts &gt; Damen</g:product_type>
      <g:size>M</g:size>
      <g:target_group>Woman</g:target_group>
      <g:material>Cotton</g:material>
      <g:weight>0.5</g:weight>
      <g:purchaseUnit>1</g:purchaseUnit>
      <g:referenceUnit>1</g:referenceUnit>
    </item>
  </channel>
</rss>
```

**Hinweise zum Format:**

| Feld             | Bedeutung                                                                                                                                                                                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `g:id`           | Eindeutige Produkt-ID. Fallback-Reihenfolge bei der Erkennung: `g:id` → `id` → `g:sku` → `sku`                                                                                                                                                                                 |
| `title`          | Produkttitel — wird zusätzlich für Suggest/Autocomplete verwendet                                                                                                                                                                                                              |
| `link`           | Produkt-URL im Shop                                                                                                                                                                                                                                                            |
| `g:image_link`   | Bild-URL                                                                                                                                                                                                                                                                       |
| `g:price`        | Preis inkl. Währung, z.B. `49.99 EUR` — der numerische Wert wird automatisch extrahiert                                                                                                                                                                                        |
| `g:sale_price`   | Optionaler Aktionspreis. Ist er gesetzt, liefert die Suche ihn als `price` und den regulären Preis als `listPrice`                                                                                                                                                             |
| `g:brand`        | Marke — wird zusätzlich für Suggest verwendet                                                                                                                                                                                                                                  |
| `g:product_type` | Kategoriepfad mit `>` als Hierarchie-Trenner (z.B. `Bekleidung > Hosen > Jeans`). Daraus werden Kategorien und Kategorie-Filter aufgebaut. Alternativ werden auch `g:google_product_category` und `g:category` gelesen; mehrere Pfade pro Produkt sind möglich                 |
| Weitere Felder   | Alle übrigen Felder (z.B. `g:size`, `g:color`, `g:material`, Eigenfelder ohne Namespace) werden beim ersten Import automatisch als Attribute erkannt und können anschließend als Suchfelder und Filter konfiguriert werden (siehe [Abschnitt 10](#10-konfiguration-attribute)) |

* Der `g:`-Prefix ist optional — Felder werden mit und ohne Namespace erkannt, der Prefix wird bei der Indexierung entfernt (`g:price` → `price`).
* Leere Elemente (z.B. `<g:gtin/>`) sind erlaubt und werden ignoriert.
* Varianten werden als eigenständige `<item>`-Einträge mit jeweils eigener `g:id` übertragen.

***

### 6.6 KI-Aufbereitung: Fortschritt

```
POST https://api.insightsearch.de/api/plugin/setup/ai-preparation-progress
```

Leichtgewichtiger Poll-Endpoint für den Fortschritt der KI-Aufbereitung (Embedding-Berechnung) je Index — ohne das schwere `attributes`/`filters`/`synonymes`-Eager-Load von `setup/validate`. Funktioniert für Feed- und Push-Indizes gleichermaßen. Eine neutralere, auf einen einzelnen Index reduzierte Alternative ist [14.2](#142-index-status).

**Request Body:**

| Parameter             | Typ    | Required | Beschreibung                                                  |
| --------------------- | ------ | -------- | ------------------------------------------------------------- |
| `accountSecret`       | string | ✅        | Account-Secret                                                |
| `installation_secret` | string | ✅        | Installations-Token                                           |
| `shop_type`           | string | ✅        | Shop-Typ                                                      |
| `locale`              | string | ❌        | Sprache für `ai_preparation.steps[].label` (Default: `de-DE`) |

**Response (200):**

```json
{
  "success": true,
  "indices": [
    {
      "feed_id": 5,
      "ai_preparation_state": "running",
      "ai_preparation_progress": 62,
      "ai_preparation": {
        "state": "running",
        "percent": 62,
        "current_label": "Produkte verarbeiten",
        "steps": [
          { "key": "embedding", "label": "Produkte verarbeiten", "state": "running", "percent": 62 }
        ]
      }
    }
  ]
}
```

`ai_preparation_state`/`ai_preparation_progress` sind `null`, solange für den Index noch keine KI-Aufbereitung läuft oder abgeschlossen wurde.

***

## 7. Push-Indexierung

Die Push-Indexierung ist die **Push-Variante** der Indexierung (siehe [Pull vs. Push](#kernkonzept-feed-index--index-id)): Der Shop überträgt seine Produkte aktiv per API in den Index, ohne dass eine öffentlich erreichbare Feed-URL nötig ist. InsightSearch stößt für Push-Indizes **keine automatische Indexierung** an — Zeitpunkt und Häufigkeit der Aktualisierung bestimmt allein der Shop, indem er den Push-Zyklus erneut durchläuft. Alle Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/push/...`.

### Ablauf

```
1. register  → Index anlegen (einmalig)
2. start     → Push-Session starten
3. batch     → Produkte in Batches übertragen (beliebig viele Batches)
4. finish    → Indexierung abschließen
```

> Bei Fehlern oder Abbruch: `abort` verwenden, um die Session aufzuräumen.

***

### 7.1 Push-Index registrieren

```
POST https://api.insightsearch.de/api/plugin/push/register
```

**Request Body:**

| Parameter             | Typ    | Required | Beschreibung                                        |
| --------------------- | ------ | -------- | --------------------------------------------------- |
| `installation_secret` | string | ✅        | Installations-Token                                 |
| `account_secret`      | string | ✅        | Account-Secret                                      |
| `shop_type`           | string | ✅        | Shop-Typ (z.B. `Shopware`)                          |
| `name`                | string | ✅        | Name des Index (max. 255 Zeichen)                   |
| `language_code`       | string | ✅        | Sprachcode(s), kommagetrennt (z.B. `de-DE`)         |
| `currency`            | string | ✅        | Währungscode (max. 8 Zeichen)                       |
| `index_type`          | string | ❌        | `SEARCH` oder `RECOMMENDATIONS` (Default: `SEARCH`) |

**Response (201):**

```json
{
  "success": true,
  "index": {
    "id": 5,
    "name": "Mein Shop DE",
    "language": "de-DE",
    "currency": "EUR",
    "indexing_mode": "push"
  }
}
```

> **Wichtig:** `index.id` ist die zentrale Index-ID für alle weiteren Endpunkte (Suche, Suggest, Konfiguration, Tracking) und sollte dauerhaft gespeichert werden — siehe [Kernkonzept](#kernkonzept-feed-index--index-id). Die Registrierung erfolgt **einmalig**; für jede Aktualisierung der Produktdaten wird anschließend nur der Zyklus `start → batch → finish` wiederholt.

***

### 7.2 Push-Session starten

```
POST https://api.insightsearch.de/api/plugin/push/start
```

Legt einen neuen versionierten Index an und startet eine Push-Session. Die Session ist **2 Stunden** gültig.

**Request Body:**

| Parameter             | Typ     | Required | Beschreibung                  |
| --------------------- | ------- | -------- | ----------------------------- |
| `installation_secret` | string  | ✅        | Installations-Token           |
| `account_secret`      | string  | ✅        | Account-Secret                |
| `shop_type`           | string  | ✅        | Shop-Typ                      |
| `index_id`            | integer | ✅        | ID des Index (aus `register`) |

**Response (200):**

```json
{
  "success": true,
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Fehlercodes:**

| HTTP | Code            | Beschreibung                    |
| ---- | --------------- | ------------------------------- |
| 404  | `PUSH_ERR_2001` | Index nicht gefunden            |
| 409  | `PUSH_ERR_2002` | Session läuft bereits           |
| 500  | `PUSH_ERR_2003` | Index-Erstellung fehlgeschlagen |

***

### 7.3 Produkt-Batch übertragen

```
POST https://api.insightsearch.de/api/plugin/push/batch
```

Überträgt einen Batch von Produkten. Kann beliebig oft aufgerufen werden. Beim ersten Batch werden Attribute automatisch erkannt und das Mapping aktualisiert.

**Request Body:**

| Parameter             | Typ           | Required | Beschreibung                                          |
| --------------------- | ------------- | -------- | ----------------------------------------------------- |
| `installation_secret` | string        | ✅        | Installations-Token                                   |
| `account_secret`      | string        | ✅        | Account-Secret                                        |
| `shop_type`           | string        | ✅        | Shop-Typ                                              |
| `session_id`          | string (UUID) | ✅        | Session-ID (aus `start`)                              |
| `products`            | array         | ✅        | Array von Produkt-Objekten (min. 1, `id` Pflichtfeld) |

Jedes Produkt muss mindestens ein `id`-Feld enthalten. Alle weiteren Feed-Felder werden übergeben. Felder mit `g:`-Prefix werden automatisch normalisiert (Prefix entfernt).

**Beispiel Request:**

```json
{
  "installation_secret": "I1b2c3d4e5f6g7h8i9j0",
  "account_secret": "A1b2c3d4e5f6g7h8i9j0",
  "shop_type": "Shopware",
  "session_id": "550e8400-e29b-41d4-a716-446655440000",
  "products": [
    {
      "id": "SW10001",
      "title": "Slim Fit Jeans",
      "price": 49.99,
      "sale_price": 39.99,
      "link": "https://shop.example.com/jeans/slim-fit",
      "image_link": "https://cdn.example.com/jeans.jpg",
      "brand": "Levi's",
      "availability": "in stock"
    }
  ]
}
```

**Response (200):**

```json
{
  "success": true,
  "indexed": 1,
  "totalSoFar": 1,
  "batchCount": 1,
  "errors": []
}
```

**Fehlercodes:**

| HTTP | Code            | Beschreibung                     |
| ---- | --------------- | -------------------------------- |
| 404  | `PUSH_ERR_3001` | Session nicht gefunden           |
| 409  | `PUSH_ERR_3003` | Session nicht aktiv              |
| 410  | `PUSH_ERR_3002` | Session abgelaufen (> 2 Stunden) |
| 500  | `PUSH_ERR_3004` | Indexierung fehlgeschlagen       |

***

### 7.4 Push-Session abschließen

```
POST https://api.insightsearch.de/api/plugin/push/finish
```

Schließt die Indexierung ab und neuer Index wird live geschaltet. Ein **Deviation Guard** prüft, ob die Produktanzahl stark vom Vorwert abweicht.

**Request Body:**

| Parameter             | Typ           | Required | Beschreibung             |
| --------------------- | ------------- | -------- | ------------------------ |
| `installation_secret` | string        | ✅        | Installations-Token      |
| `account_secret`      | string        | ✅        | Account-Secret           |
| `shop_type`           | string        | ✅        | Shop-Typ                 |
| `session_id`          | string (UUID) | ✅        | Session-ID (aus `start`) |

**Response (200) — Erfolgreich:**

```json
{
  "success": true,
  "productCount": 1500,
  "promoted": true,
  "deviation": 2.1,
  "message": "Indexierung erfolgreich abgeschlossen."
}
```

**Response (422) — Deviation Guard ausgelöst:**

```json
{
  "success": false,
  "productCount": 450,
  "promoted": false,
  "deviation": 70.0,
  "message": "Indexierung abgeschlossen, aber Promotion fehlgeschlagen."
}
```

***

### 7.5 Push-Session abbrechen

```
POST https://api.insightsearch.de/api/plugin/push/abort
```

Bricht eine laufende Push-Session ab und räumt den temporären Index auf.

**Request Body:** Identisch mit [7.4](#74-push-session-abschließen)

**Response (200):**

```json
{
  "success": true
}
```

***

### 7.6 Push-Index-Status prüfen

```
POST https://api.insightsearch.de/api/plugin/push/status
```

Reiner Existenz-Check: meldet, welche der übergebenen Index-IDs für diesen Account noch existieren (nicht gelöscht wurden). Kein Session-/Fortschritts-Status — dafür siehe [7.2](#72-push-session-starten) bzw. die `finish`-Antwort.

**Request Body:**

| Parameter             | Typ             | Required | Beschreibung          |
| --------------------- | --------------- | -------- | --------------------- |
| `installation_secret` | string          | ✅        | Installations-Token   |
| `account_secret`      | string          | ✅        | Account-Secret        |
| `shop_type`           | string          | ✅        | Shop-Typ              |
| `index_ids`           | array\<integer> | ✅        | Zu prüfende Index-IDs |

**Response (200):**

```json
{
  "success": true,
  "existing_index_ids": [5, 7],
  "missing_index_ids": [6]
}
```

***

### 7.7 Push-Session nach Index abbrechen

```
POST https://api.insightsearch.de/api/plugin/push/abort-by-index
```

Wie [7.5](#75-push-session-abbrechen), aber anhand der Index-ID statt der Session-ID adressiert — praktisch, wenn die `sessionId` nicht mehr vorliegt (z.B. nach einem Plugin-Neustart). Idempotent: läuft aktuell keine Session, meldet der Call trotzdem Erfolg.

**Request Body:**

| Parameter             | Typ     | Required | Beschreibung        |
| --------------------- | ------- | -------- | ------------------- |
| `installation_secret` | string  | ✅        | Installations-Token |
| `account_secret`      | string  | ✅        | Account-Secret      |
| `shop_type`           | string  | ✅        | Shop-Typ            |
| `index_id`            | integer | ✅        | ID des Index        |

**Response (200):**

```json
{
  "success": true
}
```

***

## 8. Konfiguration: Synonyme

Synonyme werden pro Index konfiguriert und ermöglichen es, alternative Schreibweisen oder gleichbedeutende Begriffe zu verknüpfen. Alle Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/config/synonyms/...`.

**Gemeinsame Authentifizierungsfelder** (in jedem Request):

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

### 8.1 Synonym erstellen

```
POST https://api.insightsearch.de/api/plugin/config/synonyms/create
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung                                                           |
| -------------------- | ------ | -------- | ---------------------------------------------------------------------- |
| `accountSecret`      | string | ✅        | Account-Secret                                                         |
| `installationSecret` | string | ✅        | Installations-Token                                                    |
| `indicesId`          | string | ✅        | Index-ID                                                               |
| `terms`              | string | ✅        | Kommagetrennte Synonyme (min. 2 Begriffe, z.B. `"Hose, Jeans, Pants"`) |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationSecret": "I1b2c3d4e5f6g7h8i9j0",
  "indicesId": "5",
  "terms": "Hose, Jeans, Pants, Trousers"
}
```

**Response (201):**

```json
{
  "synonym": {
    "id": 42,
    "indices_id": 5,
    "terms": "hose, jeans, pants, trousers",
    "created_at": "2026-04-14T09:00:00.000000Z",
    "updated_at": "2026-04-14T09:00:00.000000Z"
  }
}
```

***

### 8.2 Synonym aktualisieren

```
POST https://api.insightsearch.de/api/plugin/config/synonyms/update
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung                 |
| -------------------- | ------ | -------- | ---------------------------- |
| `accountSecret`      | string | ✅        | Account-Secret               |
| `installationSecret` | string | ✅        | Installations-Token          |
| `indicesId`          | string | ✅        | Index-ID                     |
| `synonymeId`         | string | ✅        | ID des Synonyms              |
| `terms`              | string | ✅        | Neue kommagetrennte Synonyme |

**Response (200):**

```json
{
  "synonym": {
    "id": 42,
    "indices_id": 5,
    "terms": "hose, jeans, trousers",
    "updated_at": "2026-04-14T09:05:00.000000Z"
  }
}
```

***

### 8.3 Synonym löschen

```
POST https://api.insightsearch.de/api/plugin/config/synonyms/delete
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung                  |
| -------------------- | ------ | -------- | ----------------------------- |
| `accountSecret`      | string | ✅        | Account-Secret                |
| `installationSecret` | string | ✅        | Installations-Token           |
| `indicesId`          | string | ✅        | Index-ID                      |
| `synonymeId`         | string | ✅        | ID des zu löschenden Synonyms |

**Response (200):**

```json
{
  "status": "deleted"
}
```

**Fehlercodes (alle Synonym-Endpunkte):**

| HTTP | Beschreibung                                    |
| ---- | ----------------------------------------------- |
| 404  | Account, Installation oder Index nicht gefunden |
| 404  | Synonym nicht gefunden                          |
| 422  | Ungültiges Format (weniger als 2 Begriffe)      |

***

## 9. Konfiguration: Filter

Filter definieren, welche Produktattribute als Facetten in der Suche verfügbar sind. Alle Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/config/filters/...`.

### 9.1 Filter erstellen

```
POST https://api.insightsearch.de/api/plugin/config/filters/create
```

**Request Body:**

| Parameter            | Typ     | Required | Beschreibung                                           |
| -------------------- | ------- | -------- | ------------------------------------------------------ |
| `accountSecret`      | string  | ✅        | Account-Secret                                         |
| `installationSecret` | string  | ✅        | Installations-Token                                    |
| `indicesId`          | string  | ✅        | Index-ID                                               |
| `attribute_id`       | string  | ✅        | ID des Attributs (muss zum Index gehören)              |
| `position`           | integer | ✅        | Anzeigereihenfolge                                     |
| `name`               | string  | ✅        | Anzeigename des Filters                                |
| `type`               | string  | ✅        | Filter-Typ (z.B. `terms`, `range`)                     |
| `active`             | boolean | ❌        | Aktiv-Status (Default: `true`)                         |
| `nlp_keywords`       | string  | ❌        | Kommagetrennte Keywords für Natural-Language-Erkennung |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationSecret": "I1b2c3d4e5f6g7h8i9j0",
  "indicesId": "5",
  "attribute_id": "12",
  "position": 1,
  "name": "Farbe",
  "type": "terms",
  "active": true,
  "nlp_keywords": "Farbe, Farbton, Color"
}
```

**Response (201):**

```json
{
  "filter": {
    "id": 8,
    "indices_id": 5,
    "attribute_id": 12,
    "position": 1,
    "name": "Farbe",
    "type": "terms",
    "active": true,
    "nlp_keywords": "Farbe, Farbton, Color"
  }
}
```

***

### 9.2 Filter aktualisieren

```
POST https://api.insightsearch.de/api/plugin/config/filters/update
```

**Request Body:**

| Parameter            | Typ     | Required | Beschreibung                 |
| -------------------- | ------- | -------- | ---------------------------- |
| `accountSecret`      | string  | ✅        | Account-Secret               |
| `installationSecret` | string  | ✅        | Installations-Token          |
| `indicesId`          | string  | ✅        | Index-ID                     |
| `filterId`           | string  | ✅        | ID des Filters               |
| `attribute_id`       | string  | ✅        | Attribut-ID                  |
| `position`           | integer | ✅        | Neue Position                |
| `name`               | string  | ✅        | Neuer Anzeigename            |
| `type`               | string  | ✅        | Filter-Typ                   |
| `active`             | boolean | ❌        | Aktiv-Status                 |
| `nlp_keywords`       | string  | ❌        | NLP-Keywords (kommagetrennt) |

**Response (200):**

```json
{
  "filter": {
    "id": 8,
    "name": "Farbe",
    "position": 2,
    "active": true
  }
}
```

***

### 9.3 Filter löschen

```
POST https://api.insightsearch.de/api/plugin/config/filters/delete
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung                 |
| -------------------- | ------ | -------- | ---------------------------- |
| `accountSecret`      | string | ✅        | Account-Secret               |
| `installationSecret` | string | ✅        | Installations-Token          |
| `indicesId`          | string | ✅        | Index-ID                     |
| `filterId`           | string | ✅        | ID des zu löschenden Filters |

**Response (200):**

```json
{
  "status": "deleted"
}
```

**Fehlercodes (alle Filter-Endpunkte):**

| HTTP | Beschreibung                                              |
| ---- | --------------------------------------------------------- |
| 404  | Account, Installation, Index oder Attribut nicht gefunden |
| 404  | Filter nicht gefunden                                     |
| 422  | Filter-ID fehlt                                           |

***

## 10. Konfiguration: Attribute

Attribute sind die indizierten Produkteigenschaften eines Index. Sie werden automatisch aus dem Feed erkannt und können über die API gesteuert werden. Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/config/attributes/...`.

### Automatische Erkennung

Bei jeder Feed-Indexierung (bzw. beim ersten Push-Batch) scannt InsightSearch alle Felder des Feeds. Für jedes neue Feld wird automatisch ein Attribut angelegt:

* **Datentyp:** wird aus Feldname und Beispielwerten abgeleitet (`text`, `keyword`, `integer`, `long`, `float`, `boolean`, `date`).
* **Such-Gewichtung (`search_wight`):** wird anhand des Feldnamens vorbelegt:

| Felder                                   | Default-Gewichtung |
| ---------------------------------------- | ------------------ |
| Titel / Beschreibung                     | 10                 |
| Marke / Kategorie                        | 8                  |
| Artikelnummern (`sku`, `mpn`, `gtin`, …) | 5                  |
| Preis                                    | 4                  |
| IDs                                      | 3                  |
| Alle übrigen Felder                      | 1                  |

* **Aktiv-Status:** Nur Kernfelder werden automatisch aktiviert (`id`, `title`, `brand`, `price`, `mpn`, `gtin`, `link`, `image_link`, `keywords`, `mark_as_topseller`). **Alle übrigen Felder werden inaktiv angelegt** und müssen bei Bedarf per [`update`](#102-attribut-aktualisieren) aktiviert werden.

> **Re-Indexierungen überschreiben keine manuelle Konfiguration:** Gewichtung, Aktiv-Status und Auto-Filter-Einstellungen bestehender Attribute bleiben erhalten — nur der erkannte Datentyp wird bei Bedarf aktualisiert.

### Wirkung der Einstellungen

| Einstellung           | Wirkung                                                                                                                                                                     | Wirksam ab                                                                                      |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `active`              | Nur aktive Attribute werden indexiert und stehen für Suche, Filter und Auto-Filter zur Verfügung                                                                            | Nächste (Re-)Indexierung — die Felddaten müssen in den Index geschrieben werden                 |
| `search_wight`        | `0` = Feld wird nicht durchsucht. Werte > 0 gewichten das Feld in der Relevanzberechnung (`feldname^gewichtung`). Nur Text-/Keyword-Felder fließen in die Volltextsuche ein | Ohne Re-Indexierung (Konfigurations-Cache bis \~5 Min., siehe [Caching](#caching))              |
| `type`                | Datentyp des Felds. Numerische Typen ermöglichen Range-Filter (Slider), sind aber nicht volltext-durchsuchbar                                                               | Vollständig erst nach Re-Indexierung (Index-Mapping)                                            |
| `auto_filter_enabled` | Erkennt Attributwerte direkt in der Suchanfrage und wendet sie automatisch als Filter an (z.B. Suche `jeans blau` → Filter auf den Farbwert `blau`)                         | Nach der nächsten (Re-)Indexierung (der dafür nötige Werteindex wird beim Indexieren aufgebaut) |
| `auto_filter_match`   | Matching-Modus der Auto-Filter-Erkennung: `exact` (exakte Übereinstimmung) oder `partial` (Teil-Übereinstimmung)                                                            | Wie `auto_filter_enabled`                                                                       |

### 10.1 Attribute abrufen

```
POST https://api.insightsearch.de/api/plugin/config/attributes/list
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationSecret": "I1b2c3d4e5f6g7h8i9j0",
  "indicesId": "5"
}
```

**Response (200):**

```json
{
  "attributes": [
    {
      "id": 12,
      "indices_id": 5,
      "field_name": "color",
      "type": "keyword",
      "search_wight": 3,
      "active": true,
      "auto_filter_enabled": false,
      "auto_filter_match": "exact"
    },
    {
      "id": 13,
      "indices_id": 5,
      "field_name": "brand",
      "type": "keyword",
      "search_wight": 5,
      "active": true,
      "auto_filter_enabled": true,
      "auto_filter_match": "exact"
    }
  ]
}
```

***

### 10.2 Attribut aktualisieren

```
POST https://api.insightsearch.de/api/plugin/config/attributes/update
```

**Request Body:**

| Parameter             | Typ     | Required | Beschreibung                                                                                               |
| --------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `accountSecret`       | string  | ✅        | Account-Secret                                                                                             |
| `installationSecret`  | string  | ✅        | Installations-Token                                                                                        |
| `indicesId`           | string  | ✅        | Index-ID                                                                                                   |
| `attributeId`         | string  | ✅        | Attribut-ID                                                                                                |
| `search_wight`        | integer | ❌        | Such-Gewichtung (min. 0). Höhere Werte = stärkerer Einfluss auf Relevanz, `0` = Feld wird nicht durchsucht |
| `active`              | boolean | ❌        | Attribut aktiv/inaktiv (inaktive Attribute werden nicht indexiert)                                         |
| `type`                | string  | ❌        | Datentyp: `text`, `keyword`, `integer`, `long`, `float`, `boolean`, `date`                                 |
| `auto_filter_enabled` | boolean | ❌        | Automatische Filter-Erkennung aus der Suchanfrage                                                          |
| `auto_filter_match`   | string  | ❌        | Matching-Modus für Auto-Filter: `exact` oder `partial`                                                     |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationSecret": "I1b2c3d4e5f6g7h8i9j0",
  "indicesId": "5",
  "attributeId": "12",
  "search_wight": 5,
  "auto_filter_enabled": true,
  "auto_filter_match": "exact"
}
```

**Response (200):**

```json
{
  "attribute": {
    "id": 12,
    "field_name": "color",
    "search_wight": 5,
    "active": true,
    "auto_filter_enabled": true,
    "auto_filter_match": "exact"
  }
}
```

**Fehlercodes:**

| HTTP | Beschreibung                                              |
| ---- | --------------------------------------------------------- |
| 404  | Account, Installation, Index oder Attribut nicht gefunden |
| 422  | Attribut-ID fehlt                                         |

***

## 11. User-Tracking

Tracking-Endpunkte erfassen Nutzerinteraktionen für Statistiken, personalisierte Empfehlungen und Suchoptimierungen. Alle Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/tracking/...`.

**Gemeinsame Authentifizierungsfelder:**

| Parameter           | Typ    | Required | Beschreibung        |
| ------------------- | ------ | -------- | ------------------- |
| `accountSecret`     | string | ✅        | Account-Secret      |
| `installationToken` | string | ✅        | Installations-Token |

### 11.1 Nutzer-Event erfassen

```
POST https://api.insightsearch.de/api/plugin/tracking/user-event
```

Erfasst generische Nutzerinteraktionen wie Klicks, Produktansichten, Suchen etc.

**Request Body:**

| Parameter              | Typ     | Required | Beschreibung                                            |
| ---------------------- | ------- | -------- | ------------------------------------------------------- |
| `accountSecret`        | string  | ✅        | Account-Secret                                          |
| `installationToken`    | string  | ✅        | Installations-Token                                     |
| `eventType`            | string  | ✅        | Event-Typ (max. 64 Zeichen)                             |
| `sessionId`            | string  | ❌        | Shop-Session-ID (max. 190 Zeichen)                      |
| `customerId`           | string  | ❌        | Kunden-ID (max. 190 Zeichen)                            |
| `timestamp`            | date    | ❌        | Zeitpunkt des Events (ISO 8601)                         |
| `pageUrl`              | string  | ❌        | Aktuelle Seiten-URL                                     |
| `searchTerm`           | string  | ❌        | Suchbegriff                                             |
| `searchResultsCount`   | integer | ❌        | Anzahl Suchergebnisse                                   |
| `searchResultPosition` | integer | ❌        | Position des angeklickten Ergebnisses                   |
| `clickedIdentifier`    | string  | ❌        | Kennung des angeklickten Elements                       |
| `clickedUrl`           | string  | ❌        | URL des angeklickten Elements                           |
| `productId`            | string  | ❌        | Produkt-ID                                              |
| `category`             | string  | ❌        | Kategorie                                               |
| `brand`                | string  | ❌        | Marke                                                   |
| `price`                | numeric | ❌        | Preis                                                   |
| `searchIndexId`        | integer | ❌        | Index-ID                                                |
| `clientUserAgent`      | string  | ❌        | User-Agent (für Bot-Erkennung)                          |
| `widgetId`             | string  | ❌        | Widget-ID für Empfehlungs-Tracking                      |
| `intentStage`          | string  | ❌        | Kaufabsicht: `explore`, `consideration`, `ready_to_buy` |

**Event-Typen (`eventType`):**

Das Feld ist ein freier String (max. 64 Zeichen). Die folgenden Typen werden vom System erkannt und für Statistiken sowie personalisierte Empfehlungen ausgewertet:

| eventType             | Beschreibung                      | Bemerkung                              |
| --------------------- | --------------------------------- | -------------------------------------- |
| `CLICK_SEARCH_RESULT` | Klick auf ein Suchergebnis        | Für Statistiken und Such-Attribution   |
| `CLICK_PRODUKTPAGE`   | Aufruf einer Produktdetailseite   | Basis für personalisierte Empfehlungen |
| `CLICK_KATEGORIE`     | Aufruf einer Kategorieseite       |                                        |
| `CLICK_LANDINGPAGE`   | Aufruf der Startseite             |                                        |
| `CLICK_CHECKOUT`      | Aufruf einer Checkout-Seite       |                                        |
| `CLICK_PAGEVIEW`      | Sonstiger Seitenaufruf (Fallback) |                                        |

> **Hinweis:** `add_to_cart` und `order` haben eigene dedizierte Endpunkte ([11.4](#114-warenkorb-event-erfassen) und [11.2](#112-bestellung-erfassen)) und sollten **nicht** über `user-event` gesendet werden.

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationToken": "I1b2c3d4e5f6g7h8i9j0",
  "eventType": "CLICK_SEARCH_RESULT",
  "sessionId": "sess_abc123",
  "productId": "SW10001",
  "searchTerm": "slim fit jeans",
  "searchResultPosition": 2,
  "searchIndexId": 5,
  "clientUserAgent": "Mozilla/5.0 ..."
}
```

**Response (200):**

```json
{
  "status": "ok"
}
```

> Bots werden automatisch erkannt und ignoriert — Response bleibt `{"status": "ignored"}`.

***

### 11.2 Bestellung erfassen

```
POST https://api.insightsearch.de/api/plugin/tracking/order
```

Erfasst Bestellungen zur Conversion-Analyse und Empfehlungsoptimierung.

**Request Body:**

| Parameter                             | Typ     | Required | Beschreibung                         |
| ------------------------------------- | ------- | -------- | ------------------------------------ |
| `accountSecret`                       | string  | ✅        | Account-Secret                       |
| `installationToken`                   | string  | ✅        | Installations-Token                  |
| `customerNumber`                      | string  | ✅        | Kundennummer (max. 190 Zeichen)      |
| `orderTotal`                          | numeric | ✅        | Bestellsumme (min. 0)                |
| `items`                               | array   | ✅        | Bestellpositionen (min. 1)           |
| `items[].productId`                   | string  | ✅        | Produkt-ID                           |
| `items[].quantity`                    | integer | ❌        | Menge                                |
| `items[].unitPrice`                   | numeric | ❌        | Stückpreis                           |
| `items[].listPrice`                   | numeric | ❌        | Listenpreis                          |
| `items[].discountPct`                 | numeric | ❌        | Rabatt in Prozent (0–100)            |
| `items[].category`                    | string  | ❌        | Kategorie                            |
| `items[].brand`                       | string  | ❌        | Marke                                |
| `items[].addedFromCogiSearch`         | boolean | ❌        | Über InsightSearch-Suche hinzugefügt |
| `items[].cogiSearchTerm`              | string  | ❌        | Suchbegriff bei Hinzufügen           |
| `items[].addedFromCogiRecommendation` | boolean | ❌        | Über Empfehlung hinzugefügt          |
| `items[].cogiRecommendationType`      | string  | ❌        | Empfehlungstyp bei Hinzufügen        |
| `orderId`                             | string  | ❌        | Bestell-ID                           |
| `orderCurrency`                       | string  | ❌        | Währungscode                         |
| `sessionId`                           | string  | ❌        | Shop-Session-ID                      |
| `userId`                              | string  | ❌        | Nutzer-ID                            |
| `isPaid`                              | boolean | ❌        | Bezahlstatus                         |
| `searchIndexId`                       | integer | ✅\*      | Such-Index-ID                        |

**Beispiel Request:**

```json
{
  "accountSecret": "A1b2c3d4e5f6g7h8i9j0",
  "installationToken": "I1b2c3d4e5f6g7h8i9j0",
  "customerNumber": "KD-10042",
  "orderTotal": 89.98,
  "orderCurrency": "EUR",
  "orderId": "ORD-20260414-001",
  "searchIndexId": 5,
  "items": [
    {
      "productId": "SW10001",
      "quantity": 2,
      "unitPrice": 39.99,
      "addedFromCogiSearch": true,
      "cogiSearchTerm": "slim fit jeans"
    }
  ]
}
```

**Response (200):**

```json
{
  "status": "ok"
}
```

***

### 11.3 Bezahlstatus aktualisieren

```
POST https://api.insightsearch.de/api/plugin/tracking/order-payment-status
```

Aktualisiert den Bezahlstatus einer bereits erfassten Bestellung.

**Request Body:**

| Parameter           | Typ     | Required | Beschreibung                  |
| ------------------- | ------- | -------- | ----------------------------- |
| `accountSecret`     | string  | ✅        | Account-Secret                |
| `installationToken` | string  | ✅        | Installations-Token           |
| `orderId`           | string  | ✅        | Bestell-ID (max. 128 Zeichen) |
| `isPaid`            | boolean | ✅        | Neuer Bezahlstatus            |
| `searchIndexId`     | integer | ❌        | Index-ID                      |

**Response (200):**

```json
{
  "status": "ok"
}
```

**Response (404) — Bestellung nicht gefunden:**

```json
{
  "status": "not_found"
}
```

***

### 11.4 Warenkorb-Event erfassen

```
POST https://api.insightsearch.de/api/plugin/tracking/cart
```

Erfasst "In den Warenkorb"-Events.

**Request Body:**

| Parameter           | Typ     | Required | Beschreibung                                            |
| ------------------- | ------- | -------- | ------------------------------------------------------- |
| `accountSecret`     | string  | ✅        | Account-Secret                                          |
| `installationToken` | string  | ✅        | Installations-Token                                     |
| `sessionId`         | string  | ✅        | Shop-Session-ID (max. 190 Zeichen)                      |
| `type`              | string  | ✅        | Derzeit nur: `add_to_cart`                              |
| `items`             | array   | ✅        | Produkte (min. 1)                                       |
| `items[].productId` | string  | ✅        | Produkt-ID                                              |
| `items[].quantity`  | integer | ❌        | Menge                                                   |
| `items[].unitPrice` | numeric | ❌        | Stückpreis                                              |
| `searchIndexId`     | integer | ✅\*      | Index-ID                                                |
| `customerId`        | string  | ❌        | Kunden-ID                                               |
| `intentStage`       | string  | ❌        | Kaufabsicht: `explore`, `consideration`, `ready_to_buy` |

**Response (200):**

```json
{
  "status": "ok"
}
```

***

### 11.5 Bestellungen nachtragen (Backfill)

Für den Kaltstart von Empfehlungs-Signalen (Bestseller/Trends/FBT/Personalisierung) können historische Bestellungen (bis zu 90 Tage) nachträglich importiert werden. Nutzt dieselbe Speicher-Pipeline wie [11.2](#112-bestellung-erfassen), markiert die Dokumente aber als `source=order_backfill`. Alle Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/tracking/orders/...` und akzeptieren `installationToken` oder `installationSecret` gleichwertig.

**11.5.1 Fortschritt abfragen — `orders/backfill-status`**

| Parameter           | Typ             | Required | Beschreibung                                |
| ------------------- | --------------- | -------- | ------------------------------------------- |
| `accountSecret`     | string          | ✅        | Account-Secret                              |
| `installationToken` | string          | ✅        | Installations-Token                         |
| `engineIds`         | array\<integer> | ✅        | Zu prüfende Index-IDs (min. 1)              |
| `windowDays`        | integer         | ❌        | Zeitfenster in Tagen (max. 90, Default: 90) |

Response:

```json
{ "tracked": { "5": 340 }, "windowDays": 90 }
```

**11.5.2 Bestellungen einspielen — `orders/backfill`**

| Parameter                     | Typ     | Required | Beschreibung                       |
| ----------------------------- | ------- | -------- | ---------------------------------- |
| `accountSecret`               | string  | ✅        | Account-Secret                     |
| `installationToken`           | string  | ✅        | Installations-Token                |
| `engineId`                    | integer | ✅        | Index-ID                           |
| `orders`                      | array   | ✅        | Bestellungen (max. 100 pro Aufruf) |
| `orders[].orderId`            | string  | ✅        | Bestell-ID                         |
| `orders[].orderTotal`         | numeric | ✅        | Bestellsumme                       |
| `orders[].orderCurrency`      | string  | ❌        | Währungscode                       |
| `orders[].items`              | array   | ✅        | Positionen (min. 1)                |
| `orders[].items[].productId`  | string  | ✅        | Produkt-ID                         |
| `orders[].items[].quantity`   | integer | ❌        | Menge                              |
| `orders[].items[].unit_price` | numeric | ❌        | Stückpreis                         |

Serverseitig über `index_id` + `orderId` dedupliziert — ein wiederholter Aufruf (z.B. nach Abbruch) erzeugt keine Duplikate. Response:

```json
{ "received": 50, "indexed": 47, "duplicates": 3, "failed": 0 }
```

**11.5.3 Bereits getrackte Bestellungen abfragen — `orders/backfill-existing`**

Erlaubt es, vor einem Lauf nur noch fehlende `orderId`s zu senden statt jedes Mal das komplette Zeitfenster.

| Parameter           | Typ            | Required | Beschreibung                       |
| ------------------- | -------------- | -------- | ---------------------------------- |
| `accountSecret`     | string         | ✅        | Account-Secret                     |
| `installationToken` | string         | ✅        | Installations-Token                |
| `engineId`          | integer        | ✅        | Index-ID                           |
| `orderIds`          | array\<string> | ✅        | Zu prüfende Bestell-IDs (max. 500) |

Response:

```json
{ "existing": ["ORD-1001", "ORD-1002"] }
```

**11.5.4 Backfill-Lauf abschließen — `orders/backfill-finished`**

Stößt entprellt (10 Min. Debounce je Index) eine Neuberechnung von Bestseller/Trends/FBT an, damit die frisch importierten Bestellungen berücksichtigt werden.

| Parameter           | Typ             | Required | Beschreibung         |
| ------------------- | --------------- | -------- | -------------------- |
| `accountSecret`     | string          | ✅        | Account-Secret       |
| `installationToken` | string          | ✅        | Installations-Token  |
| `engineIds`         | array\<integer> | ✅        | Betroffene Index-IDs |

Response:

```json
{ "dispatched": [5] }
```

**11.5.5 Verwaiste Tracking-Daten neu zuordnen — `orders/backfill-repoint`**

Zusatz-Absicherung vor einem Backfill-Start: verteilt Tracking-Dokumente, deren Ursprungs-Index nicht mehr existiert, inhaltsbasiert auf die aktuell lebenden Indizes. Läuft ohnehin automatisch bei jedem Reindex sowie täglich — dieser Call ist nur eine On-Demand-Variante.

| Parameter           | Typ    | Required | Beschreibung        |
| ------------------- | ------ | -------- | ------------------- |
| `accountSecret`     | string | ✅        | Account-Secret      |
| `installationToken` | string | ✅        | Installations-Token |

Response:

```json
{ "success": true, "repointed": 12 }
```

***

### 11.6 Tracking-Daten auslesen

Lese-Endpunkte auf den erfassten User-Tracking-Events (aus [11.1](#111-nutzer-event-erfassen) etc.) — z.B. für ein Debug-/Analyse-Panel im Shop-Backend. Alle liegen unter `https://api.insightsearch.de/api/plugin/tracking/...`.

**11.6.1 Sessions auflisten — `POST /sessions`**

| Parameter           | Typ    | Required | Beschreibung        |
| ------------------- | ------ | -------- | ------------------- |
| `accountSecret`     | string | ✅        | Account-Secret      |
| `installationToken` | string | ❌        | Installations-Token |

Response (Kurzfassung, max. 200 Sessions, neueste zuerst):

```json
{
  "ok": true,
  "hasData": true,
  "sessions": [
    {
      "session_id": "abc123",
      "user_id": "cust-42",
      "total_events": 14,
      "clicks": 3,
      "first_seen": "2026-07-14T07:55:00+00:00",
      "last_seen": "2026-07-14T08:10:00+00:00",
      "last_event_type": "CLICK_SEARCH_RESULT",
      "last_page_url": "https://shop.example.com/jeans",
      "last_page_title": "Jeans"
    }
  ],
  "error": null
}
```

**11.6.2 Session-Details — `POST /sessions/{sessionId}`**

`sessionId` wird URL-kodiert als Pfad-Segment übergeben. Body wie 11.6.1 (nur die Auth-Felder).

Response:

```json
{
  "ok": true,
  "session_id": "abc123",
  "summary": {
    "first_seen": "2026-07-14T07:55:00+00:00",
    "last_seen": "2026-07-14T08:10:00+00:00",
    "total_events": 14,
    "clicks": 3,
    "searches": 2,
    "pages": 5,
    "event_types": { "page_view": 5, "CLICK_SEARCH_RESULT": 3 }
  },
  "events": [
    {
      "timestamp": "2026-07-14T07:55:00+00:00",
      "event_type": "CLICK_SEARCH_RESULT",
      "index_id": "5",
      "session_id": "abc123",
      "user_id": "cust-42",
      "search_term": "jeans",
      "search_result_position": 2,
      "product_id": "SW10001"
    }
  ],
  "error": null
}
```

**11.6.3 Events eines Nutzers — `POST /user-events`**

| Parameter           | Typ     | Required | Beschreibung                                           |
| ------------------- | ------- | -------- | ------------------------------------------------------ |
| `accountSecret`     | string  | ✅        | Account-Secret                                         |
| `installationToken` | string  | ❌        | Installations-Token                                    |
| `userId`            | string  | ✅        | Kunden-/User-ID (`customerId` aus den Tracking-Events) |
| `limit`             | integer | ❌        | Max. Anzahl Events (1–1000, Default: 200)              |

Response:

```json
{
  "ok": true,
  "user_id": "cust-42",
  "count": 14,
  "events": [ { "timestamp": "2026-07-14T07:55:00+00:00", "event_type": "CLICK_SEARCH_RESULT", "...": "..." } ],
  "error": null
}
```

***

## 12. Statistiken

Alle Statistik-Endpunkte liegen unter `https://api.insightsearch.de/api/plugin/statistics/...`.

### 12.1 Such-Statistiken

```
POST https://api.insightsearch.de/api/plugin/statistics/
```

Liefert aggregierte Statistiken zu Suchen, Impressionen und Conversions.

**Request Body:** Enthält `accountSecret`, `installationToken` sowie Filterzeitraum (`from`, `to`, `period`).

***

### 12.2 Produkt-Statistiken

```
POST https://api.insightsearch.de/api/plugin/statistics/product
```

Liefert produktbezogene Kennzahlen (Impressionen, Klicks, Conversions pro Produkt).

***

### 12.3 Widget-Statistiken

```
POST https://api.insightsearch.de/api/plugin/statistics/widgets
```

Liefert Statistiken zu Empfehlungs-Widgets.

***

### 12.4 Suchvorschläge-Statistiken

```
POST https://api.insightsearch.de/api/plugin/statistics/search-suggestions
```

Liefert Top-Suchbegriffe, Null-Treffer-Suchen und ähnliche Insights.

***

### 12.5 Nutzungsstatistiken

```
POST https://api.insightsearch.de/api/plugin/statistics/usage
```

Liefert Nutzungsdaten zur Abrechnung und Quotenverwaltung.

***

## 13. Status & Probleme

```
POST https://api.insightsearch.de/api/plugin/status/problems
```

Liefert alle **offenen Probleme** des Accounts — gedacht für Status- und Warnanzeigen im Shop-Backend (z.B. im Plugin-Dashboard). Beim Aufruf prüft das System zusätzlich aktiv, ob mindestens ein Index existiert und ob die Plugin-Installationen noch KeepAlive-Signale senden — entsprechende Probleme werden dabei direkt erzeugt bzw. geschlossen.

Behobene Probleme werden automatisch aufgelöst und erscheinen nicht mehr in der Liste.

**Request Body:**

| Parameter             | Typ    | Required | Beschreibung        |
| --------------------- | ------ | -------- | ------------------- |
| `account_secret`      | string | ✅        | Account-Secret      |
| `installation_secret` | string | ✅        | Installations-Token |

**Response (200):**

```json
{
  "success": true,
  "problems": [
    {
      "id": 17,
      "type": "feed_index_failed",
      "message": "Feed-URL nicht ladbar: HTTP 404",
      "created_at": "2026-06-10T08:30:00+00:00",
      "indices_id": 5,
      "plugin_installations_id": 3,
      "data": {}
    }
  ]
}
```

| Feld                                 | Beschreibung                                                    |
| ------------------------------------ | --------------------------------------------------------------- |
| `problems[].type`                    | Problem-Typ (siehe Tabelle unten)                               |
| `problems[].message`                 | Menschenlesbare Beschreibung des Problems                       |
| `problems[].indices_id`              | Betroffener Index, `null` wenn accountweit                      |
| `problems[].plugin_installations_id` | Betroffene Installation, `null` wenn nicht installationsbezogen |
| `problems[].data`                    | Zusätzliche Detail-Daten je nach Problem-Typ                    |

**Problem-Typen (`type`):**

| Typ                     | Bedeutung                                                                                                                                                        |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `no_index_present`      | Für den Account wurde noch kein Index angelegt                                                                                                                   |
| `feed_index_failed`     | Die letzte Feed-Indexierung ist fehlgeschlagen (`message` enthält den Grund)                                                                                     |
| `feed_bulk_item_errors` | Einzelne Produkte konnten bei der Indexierung nicht übernommen werden                                                                                            |
| `plugin_keepalive`      | Die Plugin-Installation sendet keine KeepAlive-Signale mehr (über 72 Stunden kein Kontakt oder noch nie)                                                         |
| `broken_filter`         | Ein Filter-Feld verursacht Fehler in der Suche — wird von der Suche automatisch gemeldet (vgl. `broken_filter_fields` im Such-Response, [Abschnitt 3](#3-suche)) |

**Fehlercodes:**

| HTTP | error\_code                | Beschreibung                                                |
| ---- | -------------------------- | ----------------------------------------------------------- |
| 404  | `account_secret_not_found` | Account-Secret unbekannt                                    |
| 404  | `user_not_found`           | Zugehöriger Account nicht gefunden                          |
| 404  | `installation_not_found`   | Installations-Token unbekannt oder gehört nicht zum Account |

***

## Allgemeine Fehlercodes

| HTTP      | Beschreibung                                                                  |
| --------- | ----------------------------------------------------------------------------- |
| 401       | Authentifizierung fehlgeschlagen (ungültige Tokens)                           |
| 404       | Ressource nicht gefunden (Account, Installation, Index, Synonym, Filter etc.) |
| 409       | Konflikt (z.B. Session läuft bereits, Indexierung aktiv)                      |
| 410       | Session abgelaufen                                                            |
| 422       | Validierungsfehler (fehlende oder ungültige Felder)                           |
| 500 / 502 | Interner Serverfehler                                                         |

***

## 14. Headless Developer-API (v1)

Granulare, engine-neutrale Endpunkte für Headless-/Fremdintegrationen, additiv zu den bestehenden `/api/plugin/*`-Endpunkten. Sie liefern gezielt nur die Daten eines Bereichs (Index-Liste, Filter, Synonyme, …) statt wie `setup/validate` alles gebündelt. Response-Felder sind bewusst neutral benannt und enthalten **keine** internen oder technologie-spezifischen Angaben (kein Suchmaschinen-Name, keine Roh-Vektoren, keine internen IDs).

Alle Endpunkte liegen unter `https://api.insightsearch.de/api/v1/...` und sind `POST`.

**Auth:**

| Bereich                    | Auth-Felder                                                                                      |
| -------------------------- | ------------------------------------------------------------------------------------------------ |
| Config-Reads (`indices/*`) | `accountSecret` + `installationSecret` (wie die bestehenden `config/*`-Endpunkte)                |
| Events (`events/*`)        | `sessionToken` (wie Suche/Suggest — ein öffentliches Frontend hält damit nie das Account-Secret) |

**Fehlercodes:**

| HTTP | Code                        | Beschreibung                                                 |
| ---- | --------------------------- | ------------------------------------------------------------ |
| 401  | `ERR_INVALID_CREDENTIALS`   | `accountSecret`/`installationSecret` ungültig (Config-Reads) |
| 401  | `ERR_SESSION_TOKEN_INVALID` | `sessionToken` ungültig oder abgelaufen (Events)             |
| 404  | `ERR_INDEX_NOT_FOUND`       | Index existiert nicht oder gehört nicht zum Account          |

### 14.1 Index-Liste

```
POST https://api.insightsearch.de/api/v1/indices
```

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |

**Response (200):**

```json
{
  "indices": [
    {
      "id": 5,
      "name": "Mein Shop DE",
      "language": "de-DE",
      "currency": "EUR",
      "state": "active",
      "productCount": 1500,
      "indexType": "SEARCH",
      "indexingMode": "feed",
      "lastSyncedAt": "2026-07-14T08:00:00+00:00"
    }
  ]
}
```

***

### 14.2 Index-Status

```
POST https://api.insightsearch.de/api/v1/indices/status
```

Liefert Zustand und KI-Aufbereitungs-Fortschritt eines einzelnen Index (neutralere Alternative zu [6.6](#66-ki-aufbereitung-fortschritt)).

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung                                               |
| -------------------- | ------ | -------- | ---------------------------------------------------------- |
| `accountSecret`      | string | ✅        | Account-Secret                                             |
| `installationSecret` | string | ✅        | Installations-Token                                        |
| `indicesId`          | string | ✅        | Index-ID                                                   |
| `locale`             | string | ❌        | Sprache für `preparation.steps[].label` (Default: `de-DE`) |

**Response (200):**

```json
{
  "state": "active",
  "productCount": 1500,
  "lastSyncedAt": "2026-07-14T08:00:00+00:00",
  "preparation": {
    "state": "done",
    "progress": 100,
    "steps": [
      { "id": "embedding", "label": "Produkte verarbeiten", "state": "done" }
    ]
  }
}
```

***

### 14.3 Attribute abrufen

```
POST https://api.insightsearch.de/api/v1/indices/attributes
```

Neutrale Variante von [10.1](#101-attribute-abrufen). Nutzt `weight` statt `search_wight` und einen technologie-neutralen `type` (`string` statt `keyword`, `integer` statt `long`, `decimal` statt `float`/`scaled_float`).

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

**Response (200):**

```json
{
  "attributes": [
    {
      "field": "color",
      "type": "string",
      "weight": 3,
      "searchable": true,
      "filterable": false,
      "filterMatch": "exact"
    }
  ]
}
```

***

### 14.4 Filter abrufen

```
POST https://api.insightsearch.de/api/v1/indices/filters
```

Read-Pendant zu den bestehenden Filter-Schreib-Endpunkten ([9](#9-konfiguration-filter)) — bisher gab es dafür keinen eigenen Abruf-Call.

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

**Response (200):**

```json
{
  "filters": [
    {
      "id": 21,
      "field": "brand",
      "label": "Marke",
      "type": "checkbox",
      "position": 1,
      "active": true,
      "defaultOpen": false,
      "keywords": ["marke", "hersteller"]
    }
  ]
}
```

***

### 14.5 Synonyme abrufen

```
POST https://api.insightsearch.de/api/v1/indices/synonyms
```

Read-Pendant zu den bestehenden Synonym-Schreib-Endpunkten ([8](#8-konfiguration-synonyme)).

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

**Response (200):**

```json
{
  "synonyms": [
    { "id": 7, "terms": ["hose", "jeans"], "canonical": null },
    { "id": 8, "terms": ["notebook", "laptop"], "canonical": "notebook" }
  ]
}
```

`canonical` ist gesetzt, wenn das Synonym als explizites Mapping (`"a, b => canonical"`) angelegt wurde, sonst `null` (Gruppen-Synonym).

***

### 14.6 Feld-Schema (Discovery)

```
POST https://api.insightsearch.de/api/v1/indices/schema
```

Für Headless-Clients gedacht: welche Felder durchsuchbar/filterbar sind und welche Sortier-/Such-Modi generell verfügbar sind — ohne dass zuvor `attributes` und `filters` separat abgeglichen werden müssen.

**Request Body:**

| Parameter            | Typ    | Required | Beschreibung        |
| -------------------- | ------ | -------- | ------------------- |
| `accountSecret`      | string | ✅        | Account-Secret      |
| `installationSecret` | string | ✅        | Installations-Token |
| `indicesId`          | string | ✅        | Index-ID            |

**Response (200):**

```json
{
  "fields": [
    { "field": "brand", "type": "string", "searchable": true, "filterable": true },
    { "field": "price", "type": "decimal", "searchable": false, "filterable": true }
  ],
  "sortOptions": ["relevance", "price_asc", "price_desc", "newest", "oldest", "title_asc", "title_desc"],
  "searchModes": ["standard", "precise", "explorative"]
}
```

`sortOptions`/`searchModes` sind ein fester, für alle Indizes gleicher Satz benannter Optionen (kein Per-Feld-„sortable"-Flag) — das entspricht der tatsächlichen Fähigkeit der Such-API (siehe [3](#3-suche)).

***

### 14.7 Events: Impression

```
POST https://api.insightsearch.de/api/v1/events/impression
```

Shop-unabhängiges Pendant zur internen Impression-Erfassung — meldet, welche Produkte in einem Suchergebnis angezeigt wurden.

**Request Body:**

| Parameter           | Typ     | Required | Beschreibung                                 |
| ------------------- | ------- | -------- | -------------------------------------------- |
| `sessionToken`      | string  | ✅        | Session-Token                                |
| `indexId`           | integer | ✅        | Index-ID                                     |
| `sessionId`         | string  | ❌        | Client-Session-ID                            |
| `customerId`        | string  | ❌        | Kunden-ID                                    |
| `query`             | string  | ❌        | Suchbegriff, zu dem die Impressionen gehören |
| `items`             | array   | ✅        | Angezeigte Produkte (min. 1)                 |
| `items[].productId` | string  | ✅        | Produkt-ID                                   |
| `items[].position`  | integer | ❌        | Position im Ergebnis (0-basiert)             |

**Response (200):**

```json
{ "status": "ok" }
```

***

### 14.8 Events: Click

```
POST https://api.insightsearch.de/api/v1/events/click
```

**Request Body:**

| Parameter      | Typ     | Required | Beschreibung                         |
| -------------- | ------- | -------- | ------------------------------------ |
| `sessionToken` | string  | ✅        | Session-Token                        |
| `indexId`      | integer | ✅        | Index-ID                             |
| `sessionId`    | string  | ❌        | Client-Session-ID                    |
| `productId`    | string  | ✅        | Geklicktes Produkt                   |
| `query`        | string  | ❌        | Suchbegriff, zu dem der Klick gehört |
| `position`     | integer | ❌        | Position im Ergebnis (0-basiert)     |

**Response (200):**

```json
{ "status": "ok" }
```

***

### 14.9 Events: Order

```
POST https://api.insightsearch.de/api/v1/events/order
```

Shop-unabhängiges Pendant zu [11.2](#112-bestellung-erfassen). Eine Bestellung ohne separaten Zahlstatus-Aufruf gilt als abgeschlossen (`isPaid=true`) — für abweichende Fälle (z.B. Nachverfolgung offener Zahlungen) bleibt der bestehende [`orderPaymentStatus`](#113-bezahlstatus-aktualisieren)-Endpoint zuständig.

**Request Body:**

| Parameter           | Typ     | Required | Beschreibung                   |
| ------------------- | ------- | -------- | ------------------------------ |
| `sessionToken`      | string  | ✅        | Session-Token                  |
| `indexId`           | integer | ✅        | Index-ID                       |
| `sessionId`         | string  | ❌        | Client-Session-ID              |
| `customerId`        | string  | ❌        | Kunden-ID                      |
| `orderId`           | string  | ✅        | Bestell-ID                     |
| `currency`          | string  | ✅        | Währungscode (max. 12 Zeichen) |
| `total`             | numeric | ✅        | Bestellsumme                   |
| `items`             | array   | ✅        | Positionen (min. 1)            |
| `items[].productId` | string  | ✅        | Produkt-ID                     |
| `items[].quantity`  | integer | ❌        | Menge                          |
| `items[].price`     | numeric | ❌        | Stückpreis                     |

**Response (200):**

```json
{ "status": "ok" }
```

***

## 15. Facetten & Capabilities

Zwei schlanke Such-Endpunkte für Headless-Frontends, die Facetten/Fähigkeiten separat von einer vollen Produktsuche abfragen wollen. Beide liegen — wie Suche und Suggest — unter `https://search.insightsearch.de/api/...` und werden per `sessionToken` authentifiziert.

### 15.1 Facetten abrufen

```
POST https://search.insightsearch.de/api/facets
```

Liefert Facetten inkl. Trefferzahlen zu einer Query/Filterkombination, **ohne** Produkt-Payload — z.B. für den Aufbau einer Filter-Navigation oder Kategorie-Landingpage ohne (nochmaliges) Laden der Trefferliste.

**Request Body:**

| Parameter       | Typ     | Required | Beschreibung                                                          |
| --------------- | ------- | -------- | --------------------------------------------------------------------- |
| `sessionToken`  | string  | ✅        | Session-Token                                                         |
| `searchIndexId` | integer | ✅        | Index-ID (Alias: `engineId`)                                          |
| `query`         | string  | ✅        | Suchbegriff                                                           |
| `searchMode`    | string  | ❌        | `standard` (Default) / `precise` / `explorative`, siehe [3](#3-suche) |
| `filter`        | object  | ❌        | Aktive Filter, gleiches Format wie bei der Suche                      |

**Response (200):**

```json
{
  "total": 128,
  "facets": [
    {
      "field": "brand",
      "label": "Marke",
      "type": "terms",
      "options": [
        { "value": "Levi's", "label": "Levi's", "count": 42, "selected": false }
      ]
    },
    {
      "field": "price",
      "label": "Preis",
      "type": "range",
      "range": { "min": 9.99, "max": 199.99 }
    }
  ]
}
```

> Diese v1-Ausgabe ist bewusst schlank: anders als `/api/search` gibt es (noch) keinen Retry bei defekten Filterfeldern — ein solcher Fall liefert `502 ERR_SEARCH_BACKEND`.

***

### 15.2 Capabilities abrufen

```
POST https://search.insightsearch.de/api/capabilities
```

Discovery-Endpoint für Headless-Clients: welche Such-Modi, Sortieroptionen und Limits die Such-API generell unterstützt — unabhängig von einem konkreten Index.

**Request Body:**

| Parameter      | Typ    | Required | Beschreibung  |
| -------------- | ------ | -------- | ------------- |
| `sessionToken` | string | ✅        | Session-Token |

**Response (200):**

```json
{
  "searchModes": ["standard", "precise", "explorative"],
  "sortOptions": ["relevance", "price_asc", "price_desc", "newest", "oldest", "title_asc", "title_desc"],
  "maxPerPage": 60,
  "features": {
    "suggest": true,
    "facets": true
  }
}
```

***

## 16. Postman & Fehlersuche

So wird die API mit Postman (oder einem beliebigen HTTP-Client) korrekt angesprochen — und was die typischen Fehler bedeuten.

### 16.1 Grundregeln

* **Methode:** Alle Plugin-Endpunkte sind `POST`.
* **Pfad immer mit `/api`-Prefix:** z.B. `https://api.insightsearch.de/api/plugin/feed/validate` — **nicht** `.../plugin/feed/validate` ohne `/api`.
* **Header:**
  * `Content-Type: application/json`
  * `Accept: application/json` (empfohlen)
* **Body:** `raw` / `JSON`.

### 16.2 Authentifizierung (Feed- & Push-Endpunkte)

Jeder Request an die Feed- und Push-Endpunkte braucht **drei** Felder im JSON-Body:

| Feld                  | Beschreibung        | Hinweis                        |
| --------------------- | ------------------- | ------------------------------ |
| `installation_secret` | Installations-Token | beginnt mit **`I`**            |
| `account_secret`      | Account-Secret      | beginnt mit **`A`**            |
| `shop_type`           | Shop-Typ            | für Shopware-Shops: `Shopware` |

> **Häufigster Fehler:** `installation_secret` und `account_secret` vertauscht. Merkhilfe: **I** = **I**nstallation, **A** = **A**ccount.

> **`shop_type`:** Die Admin-UI zeigt nur die beiden Keys, **nicht** den `shop_type`. Für Shopware-Installationen ist der Wert `Shopware`. Der serverseitige Vergleich ist **case-insensitiv** — `Shopware`, `shopware` und `SHOPWARE` lösen alle dieselbe Installation auf.

### 16.3 Schnellstart (feed/validate)

```
POST https://api.insightsearch.de/api/plugin/feed/validate
Content-Type: application/json
Accept: application/json
```

```json
{
  "installation_secret": "I…(Installation Key aus Admin-UI)…",
  "account_secret": "A…(Account Key aus Admin-UI)…",
  "shop_type": "Shopware"
}
```

Eine fertige Postman-Collection inkl. Environment liegt im Repo:

* `docs/InsightSearch.postman_collection.json`
* `docs/InsightSearch.postman_environment.json`

In Postman über **Import** beide Dateien laden, das Environment **„InsightSearch"** auswählen und die Variablen `installation_secret`, `account_secret` und `shop_type` mit den Werten aus der Admin-UI füllen. Alle Requests nutzen dann `{{api_base}}` + Variablen.

### 16.4 Fehler → Ursache

| Antwort                                                      | Ursache & Lösung                                                                                                                                                                                |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `404 {"message":"Installation secret not found"}`            | `installation_secret` falsch oder mit `account_secret` vertauscht (A statt I), oder `shop_type` passt nicht zur Installation. `shop_type` ist case-insensitiv — bei Shopware `Shopware` senden. |
| `404 {"message":"Account or Installation secret not found"}` | `account_secret` gehört nicht zur gefundenen Installation (z.B. nach Neuinstallation mit neuem Installations-Secret).                                                                           |
| `401 {"code":"PUSH_ERR_1001"}`                               | Push-Endpunkt: Installation/Account nicht auflösbar (gleiche Ursachen wie 404 oben — Push antwortet generisch mit 401).                                                                         |
| `422` mit `errors`-Objekt                                    | Pflichtfeld fehlt oder ungültig (Validierung). Sind alle drei Auth-Felder **plus** die endpunkt-spezifischen Felder gesetzt?                                                                    |
| **HTML-Seite / Login-Formular** als Antwort                  | Der Request lief **nicht** unter `/api/...` (Pfad falsch, `/api`-Prefix fehlt). Unter `/api/*` liefert die API **immer** JSON — eine HTML-/Login-Antwort heißt: falscher Pfad.                  |
| `404 {"error_code":"route_not_found"}`                       | Route existiert nicht (Tippfehler im Pfad).                                                                                                                                                     |

### 16.5 Config-Endpunkte (Synonyme / Filter / Attribute)

Die Konfigurations-Endpunkte (Abschnitt 8–10) akzeptieren die Auth-Felder **sowohl** in camelCase (`accountSecret`, `installationSecret`, `indicesId`) **als auch** in snake\_case (`account_secret`, `installation_secret`). Beide Schreibweisen funktionieren.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.codegiganten.de/plugin-documentation/api-dokumentationen/insightsearch-suche/uberarbeitette-doku-v2.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
