1. Einführung
Die Plattform stellt drei getrennte Kanäle bereit. Sie teilen weder dasselbe Publikum noch dieselbe Authentifizierung noch dieselben Grenzen. Den richtigen zu wählen ist die erste Entscheidung einer Integration.
| Kanal |
Präfix |
Für wen |
Authentifizierung |
| API CMS |
/api/v1/cms/ |
E-Commerce-Module, ERP, CRM, interne Werkzeuge |
API-Schlüssel + HMAC-Signatur bei Schreibzugriffen |
| Öffentliche API |
/api/v1/public/ |
Anzeige-Widgets, JavaScript des Shops |
Keine — ratenbegrenzt, eingeschränktes CORS |
| MCP |
/api/v1/mcp |
KI-Assistenten (Claude, ChatGPT, andere) |
Derselbe Schlüssel, dieselbe Signatur wie bei der CMS-API |
Bevor Sie eine Zeile Code schreiben: prüfen Sie, ob ein Modul nicht genügt
Die Module PrestaShop und WooCommerce erledigen alles, was diese Seite beschreibt: sie übermitteln Bestellungen zum richtigen Zeitpunkt, setzen das Widget-Skript ins Theme, platzieren die Sterne auf Produktseiten und den Bewertungsblock und kümmern sich um die Signatur der Anfragen. Der Händler fügt nichts ein und schreibt nichts.
Module herunterladen →
Diese Dokumentation richtet sich also an drei Fälle: eine Plattform, für die wir noch kein Modul haben, eine Individualentwicklung oder den Anschluss eines Fremdwerkzeugs (ERP, Kundendienst, KI-Assistent) an bereits gesammelte Bewertungen.
Basisadressen
Alle URLs dieser Seite beziehen sich auf die Adresse der API. Ein Modul muss nur diese kennen: die übrigen Adressen liefert ihm GET /api/v1/cms/me, was ihm das Raten erspart und uns erlaubt, sie zu ändern, ohne bei den Händlern irgendetwas zu aktualisieren.
| Verwendung | Adresse |
| API (alle Kanäle) | https://louis.guide |
| Händlerbereich | https://louis.guide/app |
| Widget-Skript | https://louis.guide/widget/v1/avis.js |
Konventionen
- Format — JSON bei Ein- und Ausgabe, UTF-8-kodiert. Der Header
Content-Type: application/json wird bei jeder Anfrage mit Body erwartet.
- Benennung — kleine Schlangenschreibweise (
external_order_id, experienced_at), die vorherrschende Konvention der APIs, die PHP- und JavaScript-Integratoren nutzen.
- Datumsangaben — ISO 8601 mit ausdrücklicher Zeitzone bei der Eingabe (
2026-08-01T14:22:00+02:00). Bei der Ausgabe haben vollständige Datumsangaben dasselbe Format; die öffentlichen Datumsangaben einer Bewertung sind auf den Tag reduziert (2026-08-01), weil kein Widget die Uhrzeit anzeigt.
- Beträge — als Zeichenkette übermittelt (
"129.90") und nie als Gleitkommazahl: ein beim Runden verlorener Cent auf einer Bestellung wird zur Abrechnungsdifferenz.
- Bezeichner — die von uns angelegten Objekte tragen eine dauerhafte UUID. Ihre eigenen (Bestellung, Produkt, Variante) bleiben Ihre: wir schreiben sie niemals um.
-
Fehler —
immer derselbe Umschlag
{ "error": { "code": …, "message": … } }. Der code ist stabil und für Ihr Programm bestimmt, die message für den Menschen, der Fehler sucht. Siehe §10.
-
Versionierung —
das
/v1 im Pfad ist ein Vertrag. Ein optionales Feld kann jederzeit hinzukommen; kein bestehendes Feld wird umbenannt, entfernt oder verpflichtend gemacht. Ein Bruch erschiene als /v2, während die alte Version weiter ausgeliefert wird — Module laufen bei den Händlern, und niemand kann sie aus der Ferne aktualisieren.
Ihr Code muss daher Felder ignorieren, die er nicht kennt, statt bei ihrem Anblick zu scheitern.
Einen API-Schlüssel erhalten
-
Legen Sie ein Händlerkonto in dem Händlerbereich an.
- Bestätigen Sie die E-Mail-Adresse und öffnen Sie dann den Abschnitt der API-Schlüssel.
- Notieren Sie das Geheimnis: es wird nur einmal angezeigt. Ist es verloren, lässt es sich nicht wiederfinden — man legt einen neuen Schlüssel an und widerruft den alten.
Ein Installationsmodul braucht diesen Vorgang nicht: es öffnet selbst eine Anbindungsanfrage, die der Händler mit einem Klick bestätigt. Siehe §3.
2. Authentifizierung
Die CMS-API und der MCP-Server verwenden denselben Mechanismus: einen Schlüssel, der sagt, wer aufruft, und eine Signatur, die beweist, dass der Aufrufer das Geheimnis besitzt. Das sind zwei verschiedene Dinge.
| HTTP-Methode | Erforderliche Header | Warum |
GET, HEAD |
X-Api-Key |
Ein Lesezugriff verändert nichts: der Schlüssel genügt, um ihn zu erlauben. |
POST, PUT, PATCH, DELETE |
X-Api-Key, X-Timestamp, X-Signature |
Ein Schreibzugriff verpflichtet den Händler: er muss bewiesen und nicht wiederholbar sein. |
Das Geheimnis reist niemals mit
Nur die Signatur reist. Das schließt drei Türen, die keinerlei Kompromittierung des Shops verlangen: das passive Durchsickern des Geheimnisses in die Protokolle eines Vermittlers, das Wiedereinspielen einer abgefangenen Anfrage und die Veränderung des Bodys unterwegs. Es schützt hingegen nicht vor einem Shop, dessen Datenbank gestohlen wurde — dagegen hilft die Rotation der Schlüssel.
Setzen Sie das Geheimnis niemals in eine URL: URLs landen in den Protokollen aller durchlaufenen Vermittler.
2.1 Die Signatur, Schritt für Schritt
Schritt 1 — Die drei Header
| Header | Inhalt |
X-Api-Key |
Öffentlicher Bezeichner des Schlüssels, wie im Händlerbereich angezeigt. |
X-Timestamp |
Unix-Zeitstempel in Sekunden, ausschließlich Ziffern. Keine Millisekunden, kein ISO-Datum. |
X-Signature |
Das wörtliche Präfix sha256=, gefolgt vom HMAC-SHA256 in kleingeschriebenem Hexadezimal. Das Präfix gehört zum verglichenen Wert: es wegzulassen führt zur Ablehnung. |
Schritt 2 — Die zu signierende Nutzlast bilden
Vier Teile, ohne Trennzeichen aneinandergehängt, in genau dieser Reihenfolge:
charge = X-Timestamp
+ MÉTHODE HTTP en majuscules
+ chemin logique de la requête
+ corps brut de la requête
| Teil | Genaue Regel |
| Zeitstempel |
Die Zeichenkette, die mit der in X-Timestamp gesendeten identisch ist. |
| Methode |
POST, PUT… stets in Großbuchstaben. |
| Pfad |
Der Pfad ohne Schema, ohne Host, ohne Query-String, beginnend mit / — zum Beispiel /api/v1/cms/orders. Wird die API aus einem Unterverzeichnis ausgeliefert, geht dieses Installationspräfix nicht in die Signatur ein: es gehört zur Basisadresse, nicht zum logischen Pfad. |
| Body |
Die Bytefolge genau so, wie sie gesendet wird. Einmal serialisieren, diese Zeichenkette signieren, diese Zeichenkette senden. Leerer Body → leere Zeichenkette. |
Schritt 3 — Berechnen
X-Signature = "sha256=" + HMAC_SHA256(charge, secret) // hexadécimal minuscule
Schritt 4 — Ihre Umsetzung an diesem Beispiel prüfen
Diese Werte sind festgelegt, und die gezeigte Signatur ist tatsächlich die dieser Daten: liefert Ihr Code etwas anderes, liegt das Problem in Ihrem Code, nicht in unserem.
secret : sk_demo_3f9c1a7e5b2d48a6
X-Timestamp : 1786000000
méthode : POST
chemin : /api/v1/cms/reviews/9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d/response
corps : {"content":"Merci pour votre retour !"}
Zu signierende Nutzlast (eine einzige Zeile, keine zusätzlichen Leerzeichen):
1786000000POST/api/v1/cms/reviews/9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d/response{"content":"Merci pour votre retour !"}
Erwartetes Ergebnis:
X-Signature: sha256=8a9c0de10516226f965465704902e00c666892b483b45b361f4536a6b2ee36e9
Dieselbe Berechnung in einer Shell-Zeile:
printf '%s' '1786000000POST/api/v1/cms/reviews/9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d/response{"content":"Merci pour votre retour !"}' \
| openssl dgst -sha256 -hmac 'sk_demo_3f9c1a7e5b2d48a6' -r
printf und nicht echo: Letzteres hängt einen Zeilenumbruch an, was die Signatur verändert.
Schritt 5 — Ein vollständiger Aufruf mit curl
SECRET='sk_demo_3f9c1a7e5b2d48a6'
CLE='ak_demo_5c2f81b0'
TS=$(date +%s)
CHEMIN='/api/v1/cms/orders'
CORPS='{"external_order_id":"CMD-1042","experienced_at":"2026-08-01T14:22:00+02:00","customer":{"email":"claire.martin@exemple.fr","first_name":"Claire","country":"FR","locale":"fr"},"source":{"platform":"custom","plugin_version":"1.0.0"}}'
SIG=$(printf '%s' "$TS""POST""$CHEMIN""$CORPS" \
| openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
curl -X POST 'https://louis.guide/api/v1/cms/orders' \
-H "X-Api-Key: $CLE" \
-H "X-Timestamp: $TS" \
-H "X-Signature: sha256=$SIG" \
-H 'Content-Type: application/json' \
--data-raw "$CORPS"
--data-raw und nicht --data: Letzteres interpretiert bestimmte Zeichen und kann den gesendeten Body verändern und damit die Signatur ungültig machen.
2.2 PHP-Beispiel
Der minimale Client, ohne Abhängigkeit. Es ist derselbe Mechanismus wie in den Modulen für PrestaShop und WooCommerce, auf das Wesentliche reduziert.
<?php
final class ClientGuideLouis
{
public function __construct(
private string $baseUrl, // ex. 'https://louis.guide'
private string $apiKey,
private string $secret,
) {
}
/**
* @param array<string, mixed> $donnees
*
* @return array{status: int, body: array<string, mixed>}
*/
public function post(string $chemin, array $donnees): array
{
// ON SÉRIALISE UNE SEULE FOIS. La chaîne signée et la chaîne envoyée
// doivent être le même objet : réencoder juste avant l'envoi produit
// tôt ou tard une différence d'échappement, donc un 401 inexplicable.
$corps = json_encode($donnees, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
// Le chemin LOGIQUE, sans l'adresse de base : c'est lui qui est signé.
$chemin = '/' . ltrim($chemin, '/');
$signature = hash_hmac(
'sha256',
$timestamp . 'POST' . $chemin . $corps,
$this->secret,
);
$ch = curl_init(rtrim($this->baseUrl, '/') . $chemin);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $corps,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'X-Api-Key: ' . $this->apiKey,
'X-Timestamp: ' . $timestamp,
'X-Signature: sha256=' . $signature,
],
]);
$reponse = (string) curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return ['status' => $status, 'body' => json_decode($reponse, true) ?: []];
}
}
2.3 Die vier Fallen
Vier Ursachen erklären nahezu alle signature_mismatch. Von außen sehen sie alle gleich aus — daher lohnt es, sie in dieser Reihenfolge auszuschließen.
- Der Body wurde nach dem Signieren neu kodiert. Der häufigste Fall und der am schwersten zu erkennende: ein zweimal serialisiertes Array ergibt zwei verschiedene Zeichenketten, sobald es einen Akzent oder einen Schrägstrich enthält (
JSON_UNESCAPED_UNICODE, JSON_UNESCAPED_SLASHES, Reihenfolge der Schlüssel). Signieren Sie die Zeichenkette, senden Sie diese Zeichenkette, bauen Sie sie niemals neu auf.
- Der signierte Pfad trägt ein Präfix, das er nicht haben dürfte. Der signierte Pfad lautet
/api/v1/cms/orders, auch wenn die API von https://beispiel.de/plattform/api/v1/cms/orders ausgeliefert wird. Das Installationspräfix gehört zur Basisadresse. Umgekehrt signieren Sie auch nicht die vollständige URL mit Schema und Host.
-
Die Serveruhr geht falsch.
Toleranz: 300 Sekunden Abweichung, in beide Richtungen. Darüber hinaus lautet die Antwort
timestamp_out_of_range, und ihre Meldung nennt die gemessene Abweichung in Sekunden — genau die Angabe für Ihren Hoster. Dieser Fall zeigt sich oft als Integration, die "gestern noch funktionierte".
- Die Methode oder das Präfix fehlt. Die Methode geht in Großbuchstaben in die Nutzlast ein, und der Wert von
X-Signature beginnt mit sha256=. Ein nackter HMAC ohne Präfix wird abgelehnt.
Was der Query-String nicht tut
URL-Parameter (?page=2) gehen nicht in die signierte Nutzlast ein: nur der Pfad steht darin. Praktisch ist das ohne Folgen, da die signierten Endpunkte allesamt Schreibzugriffe sind, die ihre Parameter im Body tragen — eine Umsetzung, die sie der Nutzlast hinzufügte, würde jedoch scheitern.
Wiedereinspielen und Gültigkeitsfenster
Die signierte Nutzlast umfasst den Zeitstempel, die Methode, den Pfad und den Body. Eines davon wegzulassen risse eine Lücke auf: ohne den Pfad wäre eine für POST /orders gültige Signatur auf DELETE /orders wiederverwendbar; ohne den Zeitstempel wäre die Anfrage unbegrenzt wiederverwendbar.
Das Fenster von 300 Sekunden ist es, was das Wiedereinspielen begrenzt: eine abgefangene Anfrage kann darüber hinaus nicht erneut gesendet werden. Es gibt kein Verzeichnis bereits gesehener Signaturen — innerhalb dieses Fensters wird eine identische Anfrage also zweimal angenommen. Auf die Übermittlung von Bestellungen hat das keine Auswirkung, denn sie ist idempotent über external_order_id: die zweite erhält die bereits erfasste Bestellung und versendet keine zweite E-Mail.
Antworten der Authentifizierung
Alle diese Antworten tragen den Status 401.
| Code | Ursache | Zu tun |
missing_api_key |
Header X-Api-Key fehlt. |
Den Header hinzufügen. |
invalid_api_key |
Schlüssel unbekannt, widerrufen oder abgelaufen. Die Meldung ist in allen drei Fällen bewusst gleich: sie zu unterscheiden erlaubte es, massenhaft zu testen, welche Bezeichner existieren. |
Den Schlüssel im Händlerbereich prüfen oder einen neuen anlegen. |
missing_signature |
Schreibzugriff ohne Header X-Signature. |
Die Anfrage signieren (§2.1). |
missing_timestamp |
Schreibzugriff ohne Header X-Timestamp. |
Den Zeitstempel hinzufügen und ihn signieren. |
invalid_timestamp |
X-Timestamp ist keine Ziffernfolge — Millisekunden, ISO-Datum oder ein Vorzeichen. |
Einen Unix-Zeitstempel in Sekunden senden. |
timestamp_out_of_range |
Mehr als 300 Sekunden Abweichung. Die Meldung nennt den genauen Wert. |
Die Serveruhr synchronisieren (NTP). |
signature_mismatch |
Die Signatur passt nicht zur erwarteten Nutzlast. |
Die vier Fallen aus §2.3 der Reihe nach durchgehen. |
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": {
"code": "timestamp_out_of_range",
"message": "Zeitstempel außerhalb der Toleranz: +412 s Abweichung zu unserem Server (höchstens 300 s). Die Uhr Ihres Servers ist vermutlich nicht synchron."
}
}
3. Einen Shop anbinden
Diese beiden Endpunkte erlauben es einem Modul, einen Schlüssel zu beziehen, ohne dass der Händler irgendetwas abtippen muss. Das Modul eröffnet eine Anfrage, zeigt einen Link, der Händler bestätigt im Browser, und das Modul erhält beim nächsten Lesezugriff seinen Schlüssel und sein Geheimnis.
Sie sind ohne Authentifizierung, und das ist Absicht. Die Sicherheit beruht nicht auf einer Identität, sondern auf drei Dingen: die Anfrage erhält nichts, solange kein angemeldeter Händler sie bestätigt hat, das Abfrage-Token verlässt den Server des Shops nie, und das Geheimnis wird nur ein einziges Mal übergeben. Das Schlimmste, was ein böswilliger Aufruf bewirken kann, ist eine offene Anfrage, die niemand bestätigt — und die nach einer Viertelstunde verfällt.
POST
/api/v1/pairing
Ohne Authentifizierung
Eröffnet eine Anbindungsanfrage und liefert den Bestätigungslink, der dem Händler vorzulegen ist.
| Feld | Typ | Pflicht | Beschreibung |
shop_domain | Zeichenkette | ja |
Domain des Shops, z. B. shop.beispiel.de. |
platform | Zeichenkette | nein |
prestashop, woocommerce, custom… standardmäßig unknown. |
shop_name | Zeichenkette | nein |
Lesbarer Name des Shops, bei der Kontoerstellung wiederverwendet. |
platform_version | Zeichenkette | nein |
Version der Plattform, z. B. 8.1.6. |
plugin_version | Zeichenkette | nein |
Version des aufrufenden Moduls. |
shop_uid | Zeichenkette | nein |
Eindeutiger Bezeichner, der einmal bei der Installation des Moduls gezogen wird. Bei mehreren Shops dringend empfohlen: siehe §4.3.
|
curl -X POST 'https://louis.guide/api/v1/pairing' \
-H 'Content-Type: application/json' \
--data-raw '{"shop_domain":"boutique.exemple.fr","platform":"prestashop","shop_name":"Tissu Fiesta","platform_version":"8.1.6","plugin_version":"1.2.0","shop_uid":"ps-7f3a91c4"}'
HTTP/1.1 201 Created
{
"code": "4K7M-9QR3",
"poll_token": "pt_8f2c1a7e5b2d48a6c3d9e0f1a2b3c4d5",
"expires_at": "2026-08-13T15:07:44+00:00",
"approve_url": "https://louis.guide/app/raccordement/4K7M-9QR3"
}
approve_url — in einem neuen Tab des Händlerbrowsers zu öffnen, außerhalb seines Backoffice. Dort meldet er sich an oder legt sein Konto an und bestätigt dann.
poll_token — ausschließlich serverseitig aufzubewahren. Es darf niemals auf einer Seite oder in einer URL erscheinen: damit wird das Geheimnis abgeholt.
code — dem Händler anzeigbar, damit er prüfen kann, dass er die richtige Anfrage bestätigt.
Fehlercodes
| Status | Code | Ursache |
| 422 | invalid_request | shop_domain fehlt oder ist unbrauchbar. |
| 429 | rate_limited | Mehr als 10 Eröffnungen pro Stunde und IP. |
POST
/api/v1/pairing/{code}
Ohne Authentifizierung
Fragt den Zustand der Anfrage ab und übergibt den Schlüssel, sobald — und nur sobald — der Händler bestätigt hat.
Ein POST, obwohl es ein Lesezugriff ist, weil der Aufruf einen Nebeneffekt hat: er verbraucht das Geheimnis. Bei einem GET würde ein vorladender Browser oder ein Virenscanner, der Links folgt, es anstelle des Moduls verbrauchen.
| Feld | Typ | Pflicht | Beschreibung |
poll_token | Zeichenkette | ja |
Das bei der Eröffnung der Anfrage erhaltene Token. |
curl -X POST 'https://louis.guide/api/v1/pairing/4K7M-9QR3' \
-H 'Content-Type: application/json' \
--data-raw '{"poll_token":"pt_8f2c1a7e5b2d48a6c3d9e0f1a2b3c4d5"}'
Warten auf Bestätigung:
HTTP/1.1 200 OK
{ "status": "pending" }
Bestätigt — die Zugangsdaten werden nur bei diesem Aufruf übergeben:
HTTP/1.1 200 OK
{
"status": "approved",
"api_key": "ak_live_5c2f81b0",
"secret": "sk_live_3f9c1a7e5b2d48a6",
"merchant": "tissufiesta"
}
Mögliche Werte von status
| Wert | Bedeutung | Was zu tun ist |
pending | Der Händler hat noch nicht entschieden. | Weiter abfragen. |
approved | Bestätigt. Die Antwort trägt die Zugangsdaten. | Sie speichern, das Abfragen beenden. |
rejected | Der Händler hat abgelehnt. | Aufhören und es ihm mitteilen. |
expired | Fünfzehn Minuten ohne Entscheidung vergangen. | Eine neue Anfrage eröffnen. |
consumed |
Das Geheimnis wurde bereits übergeben, und das geschieht nie zweimal. Das Modul hat die Antwort verloren. |
Die Anbindung neu beginnen — das ist das sichere Verhalten. |
unknown |
Unbekannter Code oder falsches Token. Bewusst nicht unterscheidbar: sie zu trennen machte diesen Endpunkt zu einem Orakel, das verrät, welche Shops sich anbinden. |
Das Paar Code / Token prüfen. |
rate_limited | Zu häufig abgefragt (HTTP-Status 429). | Die Aufrufe weiter auseinanderziehen. |
Speichern Sie das Geheimnis sofort. Es wird nur in dieser einen Antwort übermittelt. Ein Modul, das es nicht dauerhaft sichert, muss den Händler die ganze Anbindung erneut durchlaufen lassen.
Immer 200, auch für einen Wartezustand. Das Modul fragt in einer Schleife ab: ein HTTP-Fehlercode für eine völlig normale Lage löste grundlos Alarme aus. Fragen Sie alle fünf Sekunden ab; die Grenze liegt bei 240 Aufrufen pro Viertelstunde und IP — darüber hinaus lautet die Antwort { "status": "rate_limited" } mit Status 429.
4. CMS-API (signiert)
Der Kanal der Serverintegrationen: E-Commerce-Module, ERP, CRM, interne Werkzeuge. Allen URLs ist https://louis.guide vorangestellt.
Lesen: der Schlüssel genügt. Schreiben: Schlüssel + Signatur. Die Berechnung ist in §2 ausgeführt. Die folgenden Einträge erinnern mit einem Abzeichen an die jeweilige Regelung.
Was die API nicht erlaubt und nie erlauben wird
Kein Endpunkt ändert oder löscht eine Bewertung. Der Händler kann öffentlich antworten und zur Moderation melden, mehr nicht — genau das, was sein eigener Bereich zulässt. Eine API, die mehr erlaubt als die Oberfläche, wäre eine Hintertür in der Regelkonformität, und das ist das Erste, was eine Prüfung nachsieht.
GET
/api/v1/cms/ping
API-Schlüssel
Prüft, ob ein Schlüssel funktioniert. Es ist der erste Aufruf, den man schreibt, und derjenige, den man dem Händler als Schaltfläche "Verbindung testen" anbietet: besser, er entdeckt einen fehlerhaften Schlüssel bei der Einrichtung als bei der ersten nicht übermittelten Bestellung.
Bewusst ohne Signatur. Ein Lesezugriff verändert nichts, und vor allem: dieser Endpunkt muss nutzbar bleiben, um zu beweisen, dass ein Schlüssel gut ist, während die HMAC-Umsetzung noch fehlerhaft ist. Der Ping geht durch, der Schreibzugriff nicht: das Problem liegt in der Signatur, nicht im Schlüssel.
curl 'https://louis.guide/api/v1/cms/ping' \
-H 'X-Api-Key: ak_live_5c2f81b0'
HTTP/1.1 200 OK
{
"status": "ok",
"merchant": "tissufiesta",
"api_key": "ak_live_5c2f81b0",
"server_time": "2026-08-13T14:52:07+00:00"
}
server_time wird aus einem bestimmten Grund zurückgegeben: vergleichen Sie es mit der Uhr Ihres Servers. Eine Abweichung von mehr als 300 Sekunden lässt alle Ihre signierten Schreibzugriffe scheitern (§2.3), und hier sieht man es, bevor man einen Tag daran verliert.
GET
/api/v1/cms/me
API-Schlüssel
Zustand des Kontos: Identität des Händlers, Fähigkeiten des Tarifs, Kontingent und Adressen der Plattform.
curl 'https://louis.guide/api/v1/cms/me' \
-H 'X-Api-Key: ak_live_5c2f81b0'
HTTP/1.1 200 OK
{
"merchant": {
"uuid": "0f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"slug": "tissufiesta",
"display_name": "Tissu Fiesta",
"country": "FR",
"default_locale": "fr",
"timezone": "Europe/Paris",
"status": "active"
},
"plan": {
"code": "pro",
"can_display_product_reviews": true,
"can_use_photos": true,
"can_use_reviews_api": true,
"can_remove_branding": true
},
"quota": {
"year": 2026,
"allowance": 5000,
"used": 1284,
"remaining": 3716,
"resets_at": "2027-01-01T00:00:00+00:00"
},
"urls": {
"api": "https://louis.guide",
"dashboard": "https://louis.guide/app",
"widget": "https://louis.guide/widget/v1/avis.js",
"profile": "https://louis.guide/de/m/tissufiesta"
},
"server_time": "2026-08-13T14:52:07+00:00"
}
Lesen Sie die Fähigkeiten, nicht den Namen des Tarifs
Der Block plan stellt neben dem Tarifcode auch Fähigkeiten (can_…) bereit. Prüfen Sie Erstere: ein Modul, das if (plan === 'pro') fest verdrahtet, hört an dem Tag auf, richtig zu sein, an dem ein Tarif hinzukommt oder den Namen wechselt — bei allen Händlern gleichzeitig und ohne dass einer es beheben könnte.
| Fähigkeit | Was sie steuert |
can_display_product_reviews |
Anzeige der Produktbewertungen — die Sterne auf den Produktseiten. |
can_use_photos |
Ausgabe der Kundenfotos über die API. Sie werden schon im kostenlosen Tarif gesammelt, aber nur im kostenpflichtigen ausgeliefert: bei einem kostenlosen Konto antwortet die Galerie mit einer leeren Liste, nie mit einem Fehler. |
can_use_reviews_api |
Bewertungen über die API lesen, auf Bewertungen antworten und MCP-Zugang. Die Übermittlung der Bestellungen ist davon nicht betroffen: sie ist in allen Tarifen enthalten. |
can_remove_branding |
Entfernen des Plattformhinweises auf Widgets und E-Mails. |
Der Block urls erspart das Raten
Ihre Integration muss nur eine einzige Adresse kennen: die der API. Die übrigen — Händlerbereich, Widget-Skript, öffentliche Seite des Händlers — werden hier zurückgegeben. Ein Modul, das sie aus einer einzigen Basis zusammensetzt, unterstellt, dass alles auf demselben Host lebt, was nicht mehr stimmt, sobald ein Kanal auf eine Subdomain wechselt, und erzeugt tote Links bei allen bereits installierten Händlern.
quota.remaining verdient einen Platz in Ihrer Oberfläche: bei null werden Bestellungen weiterhin angenommen, aber es geht keine Einladung mehr hinaus. Eine Warnung bei 90 % Verbrauch erspart dem Händler, es in seinen Statistiken zu entdecken.
POST
/api/v1/cms/orders
Signatur erforderlich
Der zentrale Endpunkt. Er erfasst eine Bestellung und plant die Bewertungsanfrage ein. Alles Übrige der Plattform folgt aus diesem Aufruf: ohne ihn gibt es weder Einladung noch Bewertung noch Note.
Idempotent über external_order_id
Dieselbe Referenz erneut zu senden gibt die bereits erfasste Bestellung mit Status 200 statt 201 zurück, ohne Dublette und ohne eine zweite E-Mail an den Kunden. Das Feld idempotent der Antwort ist dann true. Sie dürfen also nach einem Netzabbruch oder einer überschrittenen Wartezeit bedenkenlos erneut senden — dieses Verhalten ist jeder selbstgebauten Entdoppelungslogik vorzuziehen.
Wann aufrufen
In dem Moment, in dem die Erfahrung gemacht wird, nicht bestellt: bei der Lieferung, beim Versand je nach Ihrer Branche, oder beim Übergang in den Status, der dafür steht. experienced_at trägt dieses Datum, und es setzt die Einladungsfrist in Gang.
Body der Anfrage
Wurzel
| Feld | Typ | Pfl. | Beschreibung |
external_order_id | Zeichenkette (100) | ja |
Referenz der Bestellung bei Ihnen. Idempotenzschlüssel und fünf Jahre aufbewahrter Kaufnachweis (AFNOR §6.3). Muss über die Zeit stabil sein. |
customer | Objekt | ja |
Identität des anzusprechenden Kunden — siehe die nächste Tabelle. |
experienced_at | ISO 8601 | ja |
Datum der Lieferung oder Nutzung, mit ausdrücklicher Zeitzone. Siehe den Kasten unten: das ist nicht das Bestelldatum. |
source | Objekt | ja |
Technischer Zusammenhang des Versands — siehe weiter unten. |
items | Array (max. 200) | nein |
Artikel. Ohne sie wird keine Produktbewertung angefragt — nur die Bewertung des Geschäfts. |
amount | Dezimalzeichenkette | nein |
Gesamtbetrag, z. B. "129.90". Nie eine Gleitkommazahl. |
currency | ISO 4217 | nein |
"EUR", "CHF"… |
channel | Aufzählung | nein |
ecommerce_order (Standard), pos_transaction, qr_scan, manual, csv_import, nfc. |
location_id | Zeichenkette (100) | nein |
Die betroffene Niederlassung, wie vom Händler hinterlegt. Ein unbekannter Wert lässt die Anfrage mit einem 422 scheitern, statt die Bestellung der falschen Verkaufsstelle zuzuordnen. |
solicitation_delay_days | ganze Zahl 0–365 | nein |
Eine für diese Bestellung eigene Frist, die die Kontoeinstellung überschreibt. Nützlich, wenn derselbe Verkäufer einen Blumenstrauß versendet, zu dem morgen gefragt werden soll, und eine Matratze, zu der erst in einem Monat. Außerhalb der Grenzen wird der Wert ignoriert und die Kontoeinstellung greift — ein abwegiger Wert darf keine Bestellung kosten. |
order_status_id | Zeichenkette (20) | nein |
Status der Bestellung bei Ihnen zum Zeitpunkt des Sendens. Rein diagnostisch — wir werten ihn nicht aus —, aber es ist die einzige Angabe, mit der sich "warum hat diese Bestellung nichts ausgelöst?" beantworten lässt. |
order_status_label | Zeichenkette (120) | nein |
Lesbare Bezeichnung dieses Status. |
experienced_at: das Lieferdatum, nicht das Bestelldatum
Ein am 1. bestelltes und am 6. geliefertes Paket trägt den 6. Das ist keine Spitzfindigkeit: zwei randomisierte Studien mit mehr als 300 000 Verbrauchern (Jung, Ryu, Han & Cho, Journal of Marketing, 2023) belegen, dass eine Einladung, die versandt wird, bevor sich der Kunde ein Urteil bilden konnte, eine negative Wirkung auf die Abgabequote hat. Die Frist am Bestelldatum zu verankern heißt, planmäßig zu früh einzuladen — um die gesamte Lieferzeit.
Es ist zudem eines der drei Datumsangaben, die öffentlich neben der Bewertung stehen (AFNOR §6.3).
customer
| Feld | Typ | Pfl. | Beschreibung |
email | E-Mail (255) | ja |
Das einzige personenbezogene Datum im Klartext, das wir annehmen. Nach Ablauf der Abgabefrist gelöscht; nur der Fingerabdruck bleibt. |
country | ISO 3166-1 alpha-2 | nein* |
*Dringend empfohlen. Google berechnet seine Händlernoten je Land und verwirft Bewertungen, deren Land unbekannt ist. Diese Angabe besteht nur zum Zeitpunkt der Bestellung: ist die Adresse einmal gelöscht, ist sie endgültig unwiederbringlich, ohne jede Nachholmöglichkeit. |
locale | fr, en, nl, de, it, es | nein |
Sprache der Einladungs-E-Mail. Andernfalls die Standardsprache des Händlers — einen niederländischsprachigen Kunden auf Französisch anzusprechen lässt die Antwortquote einbrechen. |
phone | Zeichenkette (32) | nein |
Mobilnummer für die Einladung per SMS. Internationales Format (+33612345678) dringend empfohlen: es ist das einzige eindeutige. Eine nationale Nummer wird anhand von country umgesetzt; ohne bekanntes Land wird sie verworfen, ohne die Bestellung scheitern zu lassen. Siehe den Hinweis unten. |
first_name, last_name | Zeichenkette (100) | nein |
Personalisierung der Einladung und angezeigter Name des Verfassers. |
company | Zeichenkette (255) | nein |
Firmenname, für eine geschäftliche Bestellung. |
postal_code, city | Zeichenkette | nein |
Zusammen mit der E-Mail-Adresse gelöscht. |
Senden Sie die Mobilnummer nur, wenn der Händler SMS gebucht hat. Ohne diese Option wird sie empfangen und aufbewahrt, ohne dass eine Nachricht hinausgeht: ein personenbezogenes Datum, das ohne Zweck erhoben wird, was keine der beiden Seiten bei einer Prüfung rechtfertigen könnte.
source — verpflichtend
Dieser Block ist keine Statistik. Wenn ein Händler schreibt "seit dem Update gehen meine Bewertungen nicht mehr hinaus", steckt die Antwort schon darin: Version der Plattform, Version des Moduls, auslösendes Ereignis. Ihn optional zu machen liefe darauf hinaus, ihn nie zu haben — Integratoren füllen aus, was verlangt wird, nicht was angeregt wird.
| Feld | Typ | Pfl. | Beschreibung |
platform | Zeichenkette (50) | ja |
prestashop, woocommerce, shopify, magento, custom… |
platform_version | Zeichenkette (30) | nein |
Z. B. 8.1.6. |
plugin_version | Zeichenkette (30) | nein |
Version Ihrer Integration. Bei jeder Auslieferung zu erhöhen. |
trigger | Zeichenkette (100) | nein |
Ereignis hinter dem Versand, z. B. woocommerce_order_status_completed. Macht verständlich, warum eine Bestellung zu früh oder zu spät hinausgeht. |
shop_uid | Zeichenkette (80) | nein* |
*Ausschlaggebend bei mehreren Shops. Bezeichner, der einmal bei der Installation gezogen und aufbewahrt wird. Siehe den Kasten. |
shop_id | Zeichenkette (50) | nein |
Bezeichner des Shops bei der Plattform. Dient als Rückfall, wenn shop_uid fehlt. |
shop_name | Zeichenkette (255) | nein |
Lesbarer Name dieses Shops. Ohne ihn findet der Händler in seinem Bereich eine Niederlassung namens "3" und muss raten, welche das ist. |
shop_group_id, lang_id | Zeichenkette | nein |
Zur Diagnose aufbewahrt, nie ausgewertet. lang_id trennt nichts: die Sprache der Bewertung kommt aus customer.locale. |
Mehrere Shops: shop_id genügt nicht
Er lautet "1" auf jeder Ein-Shop-Installation. Ein Händler, der zwei Websites unter demselben Konto betreibt — eine Marke je Domain, ein häufiger Fall —, sendete also aus beiden "1": die beiden Shops verschmölzen zu einer Niederlassung, die Bewertungen des einen erschienen auf der Seite des anderen, und behalten würde man den Namen aus der zuletzt empfangenen Bestellung. Ein Mangel, der beim Abnahmetest an zwei echten PrestaShop-Installationen festgestellt wurde.
shop_uid löst das: ziehen Sie ihn einmal bei der Installation und bewahren Sie ihn auf. Er übersteht einen Domainwechsel ebenso wie eine Schlüsselerneuerung — die beiden anderen Unterscheidungsmerkmale, an die man zuerst denkt und die sich beide ändern.
items[] — optional, höchstens 200 Artikel
| Feld | Typ | Pfl. | Beschreibung |
external_product_id | Zeichenkette (100) | ja |
Bezeichner des Produkts in Ihrem Katalog. |
name | Zeichenkette (255) | ja |
Name des Produkts, wie er dem Kunden angezeigt wird. |
variant_id | Zeichenkette (100) | nein* |
*Das wichtigste Feld dieser Liste. Ohne ihn teilen sich der rote und der blaue Stuhl denselben Produktschlüssel: ihre Bewertungen vermischen sich, und "das Bein ist gebrochen" bezeichnet nichts mehr. Entspricht id_product_attribute (PrestaShop), der Variation (WooCommerce), der Variante (Shopify). |
variant_label | Zeichenkette (255) | nein |
Lesbare Bezeichnung: "Farbe: rot, Größe: L". |
gtin | 8 bis 14 Ziffern | nein* |
EAN-13 oder umgesetzter UPC-A. Aggregationsschlüssel zwischen Händlern und Voraussetzung von Google, um Sterne in seinen Ergebnissen anzuzeigen. |
upc, isbn, mpn | Zeichenkette | nein |
Getrennt vom GTIN geführt, weil Kataloge sie in eigenen Spalten führen. Die ISBN ist bei Büchern ausschlaggebend, wo der GTIN oft leer ist. |
sku, brand | Zeichenkette | nein |
Interne Referenz und Marke. |
category_id, category_name | Zeichenkette | nein |
Hauptkategorie in Ihrem Katalog. |
product_url, image_url | URL (500) | nein |
In der Einladungs-E-Mail verwendet: ein Produktbild verbessert die Abgabequote deutlich. |
images | Liste von URLs (max. 10) | nein |
Weitere Bilder. |
description | Zeichenkette (5000) | nein |
Empfangen, nie unverändert wieder angezeigt: es ist Ihr Text, nicht der des Verfassers der Bewertung. Er dient dazu, das Produkt bei der Moderation einzuordnen. |
tags | Liste (max. 30) | nein |
Schlüsselwörter des Produkts, je 60 Zeichen. |
meta_title, meta_description | Zeichenkette | nein |
Metadaten der Produktseite. |
quantity | ganze Zahl > 0 | nein |
Standardmäßig 1. |
unit_price | Dezimalzeichenkette | nein |
Z. B. "19.90". Als Zeichenkette, wie alle Beträge. |
Vollständiges Beispiel
POST /api/v1/cms/orders
X-Api-Key: ak_live_5c2f81b0
X-Timestamp: 1786000000
X-Signature: sha256=…
Content-Type: application/json
{
"external_order_id": "CMD-1042",
"experienced_at": "2026-08-06T09:15:00+02:00",
"amount": "129.90",
"currency": "EUR",
"order_status_id": "5",
"order_status_label": "Livré",
"customer": {
"email": "claire.martin@exemple.fr",
"first_name": "Claire",
"last_name": "Martin",
"locale": "fr",
"country": "FR",
"postal_code": "69003",
"city": "Lyon"
},
"items": [
{
"external_product_id": "REF-42",
"variant_id": "REF-42-ROUGE-L",
"variant_label": "Couleur : rouge, Taille : L",
"name": "Nappe en lin lavé",
"sku": "NAP-LIN-42",
"gtin": "3401579874120",
"brand": "Tissu Fiesta",
"quantity": 2,
"unit_price": "64.95",
"product_url": "https://boutique.exemple.fr/nappe-lin-42",
"image_url": "https://boutique.exemple.fr/img/nappe-42.jpg"
}
],
"source": {
"platform": "prestashop",
"platform_version": "8.1.6",
"plugin_version": "1.2.0",
"trigger": "actionOrderHistoryAddAfter",
"shop_uid": "ps-7f3a91c4",
"shop_id": "1",
"shop_name": "Tissu Fiesta"
}
}
Bestellung erfasst:
HTTP/1.1 201 Created
{
"uuid": "3b8e1f42-6c7d-4e9a-91b2-5d0c3a7f4e18",
"external_order_id": "CMD-1042",
"status": "pending",
"channel": "ecommerce_order",
"experienced_at": "2026-08-06T07:15:00+00:00",
"imported_at": "2026-08-06T09:41:22+00:00",
"items_count": 1,
"idempotent": false
}
Dieselbe Anfrage erneut gesendet:
HTTP/1.1 200 OK
{ "…": "…", "idempotent": true }
Die E-Mail-Adresse wird nie zurückgegeben, auch wenn Sie sie gerade übermittelt haben: jedes zurückgegebene Datum ist ein Datum, das in Ihre eigenen Protokolle durchsickern kann.
Mögliche Werte von status
| Wert | Bedeutung |
pending | Empfangen, wartet auf Einplanung. |
scheduled | Einladung eingeplant. |
solicited | Bewertungsanfrage an den Kunden gesendet. |
reviewed | Der Kunde hat seine Bewertung abgegeben. |
cancelled | Vor dem Versand storniert. |
expired | Abgabefrist ohne Bewertung verstrichen. |
Fehler
Dieser Endpunkt ist der einzige, den API Platform bedient: seine Validierungsfehler treffen daher als Liste von violations ein und nicht im Umschlag { "error": … } der übrigen API. Ihr Code muss beide Formen annehmen.
HTTP/1.1 422 Unprocessable Content
{
"status": 422,
"detail": "customer.email: \"claire.martin\" n'est pas une adresse email valide.",
"violations": [
{
"propertyPath": "customer.email",
"message": "\"claire.martin\" n'est pas une adresse email valide."
}
]
}
| Status | Ursache | Zu tun |
| 401 |
Schlüssel fehlt, ungültig oder Signatur abgelehnt. |
Siehe §2. |
| 422 |
Ein Feld fehlt oder ist fehlerhaft — siehe violations. |
Das von propertyPath bezeichnete Feld korrigieren. |
| 422 |
experienced_at liegt in der Zukunft (über einen Tag Spielraum hinaus). |
Die Zeitzone des Servers prüfen: fast immer kommt die Abweichung von dort. |
| 422 |
experienced_at liegt mehr als 90 Tage zurück. |
Siehe den Kasten unten. Um einen Bestand zu übernehmen, wenden Sie sich an den Support. |
| 422 |
Keine Niederlassung entspricht location_id. |
Die Niederlassung im Händlerbereich anlegen oder das Feld weglassen. |
| 415 |
Header Content-Type fehlt oder ist unerwartet. |
Content-Type: application/json senden. |
Warum Bestellungen, die älter als 90 Tage sind, abgelehnt werden
Das Schadensszenario ist bekannt: ein Modul wird installiert und schiebt drei Jahre Bestand auf einmal hinaus. Tausende Einladungen gehen an veraltete Adressen, die Rückläuferquote explodiert — und da alle E-Mails von unserer Domain ausgehen, bricht die Zustellbarkeit aller Händler ein, nicht nur die des Neuzugangs.
Die Ablehnung erfolgt am Eingang, mit einer klaren Meldung, statt erst bei der Einplanung: der Integrator versteht sofort, statt seine Bestellungen stillschweigend verschwinden zu sehen.
GET
/api/v1/cms/reviews
API-Schlüssel
Kostenpflichtiger Tarif
Listet die Bewertungen des Händlers auf, von der neuesten zur ältesten, jeweils mit der veröffentlichten Antwort und einer etwaigen Meldung. Über diesen Endpunkt lassen sich Bewertungen in ein ERP, ein CRM oder ein Kundendienstwerkzeug holen.
| Parameter | Standard | Beschreibung |
type | merchant |
merchant für Bewertungen des Geschäfts, product für Produktbewertungen. |
status | alle |
published, pending, awaiting_email, rejected, disputed, withdrawn. Ein unbekannter Wert wird ignoriert — der Filter greift dann nicht, statt einen Fehler zurückzugeben. |
page | 1 | Seitennummer. |
per_page | 25 |
Von 1 bis 100. Darüber hinaus wird der Wert auf 100 begrenzt. |
curl 'https://louis.guide/api/v1/cms/reviews?status=published&per_page=50' \
-H 'X-Api-Key: ak_live_5c2f81b0'
HTTP/1.1 200 OK
{
"reviews": [
{
"id": "9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"score": 4,
"title": "Livraison rapide",
"comment": "Nappe conforme, le lin est épais. Un pli à la livraison.",
"author": "Claire M.",
"language": "fr",
"country": "FR",
"status": "published",
"verified_purchase": true,
"experienced_at": "2026-08-06T07:15:00+00:00",
"submitted_at": "2026-08-13T18:02:41+00:00",
"published_at": "2026-08-13T18:04:10+00:00",
"order_reference": "CMD-1042",
"response": {
"content": "Merci Claire, nous notons pour l'emballage.",
"created_at": "2026-08-14T08:11:00+00:00",
"updated_at": null
},
"report": null
}
],
"page": 1,
"per_page": 50,
"total": 318
}
Die drei Datumsangaben, und warum es drei sind
experienced_at (die gemachte Erfahrung), submitted_at (die Abgabe) und published_at (die Veröffentlichung) sind drei verschiedene Dinge, und die AFNOR verlangt, sie unterscheiden zu können. Ein Integrator, der sie verwechselt, zeigt "vor 3 Tagen" bei einer drei Wochen alten Erfahrung. published_at ist null, solange die Bewertung nicht veröffentlicht ist.
order_reference übernimmt Ihre external_order_id: sie verknüpft die Bewertung in Ihrem System mit der Bestellung. Bei einer außerhalb einer Einladung abgegebenen Bewertung ist sie null.
Inkrementelle Synchronisierung
Fragen Sie mit status=published ab und vergleichen Sie published_at mit dem letzten Durchlauf: den gesamten Bestand bei jedem Lauf zu holen funktioniert die ersten Monate und wird dann stündlich zu einer Abfrage über mehrere Tausend Zeilen. Die Paginierung beginnt bei 1, und das Feld total gibt die Anzahl der Bewertungen an, die dem Filter entsprechen, nicht die Anzahl der Seiten.
| Status | Code | Ursache |
| 402 | plan_required |
Der Tarif des Händlers enthält die Bewertungs-API nicht. Die Bewertungen bleiben ohne Schlüssel über die Öffentliche API lesbar — was nicht dasselbe ist: diese dient der öffentlichen Anzeige, nicht dem Export. |
POST
/api/v1/cms/reviews/{uuid}/response
Signatur erforderlich
Kostenpflichtiger Tarif
Veröffentlicht eine öffentliche Antwort auf eine Bewertung des Geschäfts oder aktualisiert die bereits vorhandene. Eine Bewertung trägt nur eine Antwort: erneutes Senden ersetzt den Text.
| Feld | Typ | Pfl. | Beschreibung |
content | Zeichenkette | ja |
Text der Antwort. Bei 3000 Zeichen abgeschnitten, ohne Fehler — prüfen Sie die Länge auf Ihrer Seite, wenn Sie der Schnitt stört. |
curl -X POST 'https://louis.guide/api/v1/cms/reviews/9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d/response' \
-H 'X-Api-Key: ak_live_5c2f81b0' \
-H 'X-Timestamp: 1786000000' \
-H 'X-Signature: sha256=…' \
-H 'Content-Type: application/json' \
--data-raw '{"content":"Merci Claire, nous notons pour l’emballage."}'
HTTP/1.1 201 Created
{
"review_id": "9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"response": {
"content": "Merci Claire, nous notons pour l'emballage.",
"created": true,
"status": "published",
"published": true
}
}
Lesen Sie published, bevor Sie irgendetwas ankündigen
Der Händler stellt in seinem Bereich ein, ob von einem Programm geschriebene Antworten unmittelbar hinausgehen oder auf seine Durchsicht warten. Diese Einstellung lebt am Konto und ist kein Parameter der Anfrage: könnte der Aufrufer selbst wählen, ob er gegengelesen werden muss, wäre die Garantie nichts wert.
Folge für Ihre Oberfläche: eine angenommene Antwort ist nicht zwangsläufig sichtbar. published: false bedeutet "als Entwurf gespeichert, im Händlerbereich freizugeben" — sagen Sie das, statt ein "veröffentlicht" anzuzeigen, das die öffentliche Seite widerlegt.
201 beim Anlegen, 200 beim Aktualisieren; das Feld created gibt dieselbe Angabe im Body wieder. Eine bereits veröffentlichte Antwort bleibt veröffentlicht: eine Aktualisierung setzt sie nie in den Entwurf zurück, was sie ohne Entscheidung von irgendjemandem von der Seite verschwinden ließe.
| Status | Code | Ursache |
| 402 | plan_required | Tarif ohne Antworten auf Bewertungen. |
| 404 | review_not_found |
Bezeichner unbekannt, fehlerhaft oder zu einem anderen Händler gehörend — alle drei Fälle sind nicht unterscheidbar, und die Abschottung verlangt es. |
| 422 | content_required | content fehlt oder ist leer. |
POST
/api/v1/cms/reviews/{uuid}/report
Signatur erforderlich
Meldet eine Bewertung zur Moderation. Die Bewertung erhält den Status "bestritten", und der Vorgang reiht sich in die Prüfung ein.
| Feld | Typ | Pfl. | Beschreibung |
reason | Aufzählung | ja |
Grund, aus der folgenden Liste zu wählen. |
detail | Zeichenkette | nein |
Angaben für den Moderator, bei 1000 Zeichen abgeschnitten. Hier schreibt man "Bestellung Nr. X, nie an diese Adresse geliefert" — eine begründete Meldung wird schneller bearbeitet. |
Zulässige Gründe
| Wert | Wann anzuführen |
inappropriate_content | Beleidigung, hasserfüllte Äußerungen, rechtswidriger Inhalt. |
spam_or_advertising | Werbung, kommerzieller Link, automatisch erzeugter Inhalt. |
off_topic | Ohne Bezug zur gemachten Erfahrung — der Spediteur, das Wetter. |
conflict_of_interest | Wettbewerber, ehemaliger Mitarbeiter, bezahlte Bewertung. |
personal_data_disclosure | Die Bewertung legt personenbezogene Daten offen. |
Eine schlechte Note ist kein Grund
Kein Grund erlaubt es, eine Bewertung wegen ihrer Note zu bestreiten, und das ist kein Versehen: dieses Verbot macht den Unterschied zwischen einer Bewertungsplattform und einem Schaufenster. Eine schlecht begründete Meldung wird abgewiesen, und die Bewertung bleibt online.
HTTP/1.1 202 Accepted
{
"review_id": "9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"report": {
"reason": "off_topic",
"status": "pending",
"review_remains_visible": true
}
}
review_remains_visible ist stets true, und das Feld besteht, damit keine Oberfläche vom Gegenteil ausgeht: die Bewertung bleibt während der gesamten Prüfung öffentlich. Sie auf eine bloße Meldung hin zu entfernen liefe darauf hinaus, den Händler abwerten zu lassen, was ihm missfällt — Google verbietet das ausdrücklich, die AFNOR ebenso. Die Antwort ist ein 202: die Anfrage ist erfasst, nicht entschieden.
| Status | Code | Ursache |
| 404 | review_not_found | Bezeichner unbekannt, fehlerhaft oder außerhalb Ihres Kontos. |
| 409 | already_reported | Zu dieser Bewertung ist bereits eine Meldung offen. |
| 422 | invalid_reason |
Grund fehlt oder steht nicht in der Liste. Die Meldung nennt die zulässigen Werte. |
5. Öffentliche API
Nur lesend, ohne Authentifizierung, unter /api/v1/public/. Das nutzen die Widgets, und das kann jede maßgeschneiderte Anzeige nutzen.
Der {slug} in den Pfaden ist der öffentliche Bezeichner des Händlers — derselbe wie auf seiner öffentlichen Seite, sichtbar in urls.profile, das /cms/me zurückgibt.
Was eine API ohne Schlüssel schützt
Es gibt keine Identität zu prüfen: dieser Code läuft bei den Besuchern eines Shops, dort kann kein Geheimnis leben. Der Schutz beruht daher auf drei anderen Dingen, die man vor der Integration kennen muss.
-
Hier treten keine sensiblen Daten aus. Keine E-Mail, kein E-Mail-Fingerabdruck, keine Bestellreferenz, kein interner Bezeichner. Ein Widget zeigt öffentliche Bewertungen; alles, was über diesen Kanal austritt, ist für jedermann lesbar.
-
Ratenbegrenzt auf 60 Anfragen pro Minute und IP-Adresse, über ein gleitendes Fenster. Eine Produktseite macht zwei Aufrufe: das lässt 30 Seitenaufrufe pro Minute von derselben Adresse zu — großzügig für einen Besucher, eng für einen Inhaltssauger. Siehe §9.
-
CORS auf die hinterlegten Domains des Händlers beschränkt. Ein Platzhalter
* erlaubte jeder Website — Wettbewerber, Vergleichsportal, Fälscher —, die Bewertungen eines beliebigen Händlers so anzuzeigen, als wären es die eigenen.
CORS: was zu hinterlegen ist, damit der Browser die Antwort annimmt
Der Header Access-Control-Allow-Origin wird nur gesetzt, wenn der aufrufende Ursprung einer Domain entspricht, die dem in der URL genannten Händler zugeordnet ist. Subdomains werden akzeptiert: eine als beispiel.de hinterlegte Domain erlaubt www.beispiel.de und shop.beispiel.de.
Das typische Symptom einer nicht hinterlegten Domain: die Anfrage geht hinaus, der Server antwortet mit 200, und der Browser blockiert das Lesen in der Konsole. Die Abhilfe liegt im Händlerbereich, nicht im Code.
Ein Aufruf von Server zu Server ist davon nicht betroffen: ohne Origin-Header gibt es keine CORS-Prüfung. Diesen Fall deckt die Ratenbegrenzung ab. CORS schützt den Browser vor einer anderen Website, nie die Daten selbst.
Cache
Alle Antworten sind öffentlich und werden zwischengespeichert: 60 Sekunden für Bewertungen und Noten, 300 Sekunden für die Anzeigeeinstellungen. Das fängt den Verkehr eines Shops in einer Aktion auf, ohne für die Spitze auslegen zu müssen. Bauen Sie keine Anzeige, die davon ausgeht, dass eine veröffentlichte Bewertung sofort erscheint.
GET
/api/v1/public/merchants/{slug}/score
Ohne Authentifizierung
Gesamtnote des Shops und Verteilung nach Note.
curl 'https://louis.guide/api/v1/public/merchants/tissufiesta/score'
HTTP/1.1 200 OK
Cache-Control: public, max-age=60
{
"merchant": { "slug": "tissufiesta", "name": "Tissu Fiesta" },
"score": {
"average": 4.6,
"count": 318,
"distribution": { "1": 4, "2": 6, "3": 18, "4": 91, "5": 199 },
"scale": { "min": 1, "max": 5 }
}
}
scale wird ausdrücklich zurückgegeben statt vorausgesetzt: ein Integrator, der "von 10" programmiert, weil sein früherer Dienstleister das war, erzeugt eine falsche Anzeige, die niemand gegenliest. count zählt nur die öffentlich sichtbaren Bewertungen.
404 merchant_not_found, wenn der Slug unbekannt ist.
GET
/api/v1/public/merchants/{slug}/reviews
Ohne Authentifizierung
Bewertungen des Geschäfts, von der neuesten zur ältesten.
| Parameter | Standard | Beschreibung |
page | 1 | Seitennummer. |
per_page | 10 |
Von 1 bis 50. Darüber hinaus auf 50 begrenzt. |
HTTP/1.1 200 OK
{
"reviews": [
{
"id": "9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"author": "Claire M.",
"score": 4,
"title": "Livraison rapide",
"comment": "Nappe conforme, le lin est épais. Un pli à la livraison.",
"language": "fr",
"verified_purchase": true,
"experienced_at": "2026-08-06",
"submitted_at": "2026-08-13",
"published_at": "2026-08-13",
"disputed": false,
"photos": [
{
"id": "6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f",
"url": "https://louis.guide/photos/6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f",
"thumbnail_url": "https://louis.guide/photos/6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f/thumb",
"width": 1600,
"height": 1200
}
],
"reply": {
"content": "Merci Claire, nous notons pour l'emballage.",
"published_at": "2026-08-14"
}
}
],
"page": 1,
"per_page": 10
}
Die chronologische Reihenfolge ist vorgeschrieben, nicht gewählt
Es gibt keinen Sortierparameter, und es wird keinen geben: AFNOR (§6.3) verlangt die umgekehrt chronologische Reihenfolge als Standardanzeige. "Bestbewertete zuerst" als Ausgangssortierung anzubieten wäre eine gefärbte Darstellung. Eine Sortierung in JavaScript auf der empfangenen Seite liegt in Ihrer Verantwortung, nicht in unserer.
Was Ihre Anzeige übernehmen muss
-
Mindestens zwei Datumsangaben — die der Erfahrung und die der Veröffentlichung. Das ist eine Anzeigepflicht, und nur die API kann sie Ihnen liefern. Die öffentlichen Datumsangaben sind auf den Tag reduziert (
2026-08-06): kein Widget zeigt die Uhrzeit.
verified_purchase — die Bewertung ist an eine tatsächliche Bestellung geknüpft. Das unterscheidet eine eingeholte Bewertung von einer spontan abgegebenen.
-
disputed — die Bewertung ist bestritten und ihre Prüfung läuft. Sie bleibt angezeigt (siehe §4.6); kennzeichnen Sie sie, statt sie zu verbergen.
reply — die Antwort des Händlers gehört für den Leser zur Bewertung. published_at trägt dort das Datum der letzten Änderung, sofern es eine gab: das ursprüngliche Datum unter einem neu geschriebenen Text anzuzeigen führte in die Irre.
photos — leer bei einem Händler, dessen Tarif sie nicht ausliefert. Die Bewertungen bleiben vollständig, nur die Bilder fehlen.
Auf diesem Kanal gibt es kein Feld total: eine leere Seite bedeutet, dass nichts mehr zu laden ist. Genau das tut die Schaltfläche "mehr anzeigen" des Widgets.
GET
/api/v1/public/products/{slug}/{productId}/score
Ohne Authentifizierung
Note eines Produkts. {productId} ist Ihr Katalogbezeichner, derjenige, der als external_product_id übermittelt wurde — wir schreiben ihn nie um. Denken Sie daran, ihn zu kodieren, wenn er reservierte Zeichen enthält.
| Parameter | Standard | Beschreibung |
variant | — |
Beschränkt die Note auf eine Variante. Fehlt er, bezieht sich die Note auf alle Varianten zusammen — was das richtige Verhalten ist, solange der Besucher seine Größe nicht gewählt hat. |
with_variants | false |
Fügt die Aufschlüsselung nach Variante hinzu. Kostet eine zusätzliche Abfrage: schalten Sie es nicht auf der Produktseite ein, der meistbesuchten Seite des Shops. |
curl 'https://louis.guide/api/v1/public/products/tissufiesta/REF-42/score?with_variants=true'
HTTP/1.1 200 OK
{
"product": { "external_id": "REF-42", "variant_id": null },
"score": {
"average": 4.4,
"count": 27,
"distribution": { "1": 0, "2": 1, "3": 3, "4": 7, "5": 16 },
"scale": { "min": 1, "max": 5 }
},
"variants": [
{ "variant_id": "REF-42-ROUGE-L", "average": 4.8, "count": 12 },
{ "variant_id": "REF-42-BLEU-M", "average": 4.1, "count": 15 }
]
}
variants ist null, wenn with_variants nicht angefordert wurde — das ist ein Fehlen der Berechnung, kein Fehlen von Varianten.
GET
/api/v1/public/products/{slug}/{productId}/reviews
Ohne Authentifizierung
Bewertungen eines Produkts. Dieselbe Antwortstruktur und dieselben Paginierungsparameter wie bei den Bewertungen des Geschäfts, dazu der Filter variant — nützlich, wenn eine Größenauswahl nur die Bewertungen der gewählten Variante zeigen soll.
curl 'https://louis.guide/api/v1/public/products/tissufiesta/REF-42/reviews?variant=REF-42-ROUGE-L&per_page=5'
Sehen Sie bei einem Händler, dessen Tarif die Anzeige von Produktbewertungen nicht enthält, eine Anzeige vor, die sauber zurücktritt, statt eines leeren Rahmens: das Widget selbst verschwindet von der Seite.
GET
/api/v1/public/merchants/{slug}/photos
Ohne Authentifizierung
Freigegebene Kundenfotos, ohne den Text der Bewertungen. Das speist ein Karussell: ohne diesen Endpunkt müsste man fünfzig vollständige Bewertungen laden — Text, Datumsangaben, Noten —, um daraus nur die Bilder zu behalten, auf einer Produktseite, die ohnehin schon das Theme des Händlers lädt.
| Parameter | Standard | Beschreibung |
produit | — |
Beschränkt auf ein Produkt. Fehlt er, werden die Fotos des ganzen Shops zurückgegeben — was ein Karussell auf der Startseite speist. |
limite | 24 |
Von 1 bis 50. |
HTTP/1.1 200 OK
{
"photos": [
{
"id": "6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f",
"url": "https://louis.guide/photos/6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f",
"thumbnail_url": "https://louis.guide/photos/6d2a1f80-9b3c-4d5e-8f70-1a2b3c4d5e6f/thumb",
"width": 1600,
"height": 1200
}
]
}
Die URLs sind absolut: dieses JSON wird von JavaScript gelesen, das auf der Domain des Shops läuft, wo eine relative URL auf den Shop selbst zeigte. Nutzen Sie width und height, um den Platz vor dem Laden zu reservieren — sonst springt die Produktseite vor den Augen des Besuchers.
Bei einem Händler, dessen Tarif die Fotos nicht ausliefert, lautet die Antwort { "photos": [] } mit Status 200, nie ein Fehler: das Karussell verschwindet sauber, statt einen fehlgeschlagenen Rahmen zu zeigen.
GET
/api/v1/public/merchants/{slug}/display
Ohne Authentifizierung
Anzeigeeinstellungen, die der Händler in seinem Bereich festlegt. Damit lassen sich die Tags ein für alle Mal in ein Theme setzen und danach ein Element ein- oder ausschalten oder verschieben, ohne den Code des Shops anzurühren.
HTTP/1.1 200 OK
Cache-Control: public, max-age=300
{
"merchant": {
"slug": "tissufiesta",
"name": "Tissu Fiesta",
"accent_color": "#7c3aed"
},
"display": {
"badge_flottant": false,
"badge_cote": "droite",
"badge_decalage": 16,
"seuil_avis": 1,
"etoiles_fiche": true,
"etoiles_vignettes": true,
"onglet_avis": true,
"bloc_accueil": true
},
"profile_url": "https://louis.guide/de/m/tissufiesta"
}
| Einstellung | Standard | Bedeutung |
badge_flottant | false |
In einer Bildschirmecke fixiertes Bewertungsabzeichen. Standardmäßig aus: es legt sich über die Seite des Händlers, und bei ihm darf nichts erscheinen, ohne dass er darum gebeten hat. |
badge_cote | droite | droite oder gauche. |
badge_decalage | 16 | Abstand in Pixeln zum Rand. |
seuil_avis | 1 |
Anzahl der Bewertungen, unter der die Anzeige verschwindet. Siehe §6: "keine Bewertungen" anzuzeigen ist schlimmer, als nichts anzuzeigen. |
etoiles_fiche | true | Sterne auf der Produktseite. |
etoiles_vignettes | true | Sterne auf den Vorschaubildern in Listen. |
onglet_avis | true | Reiter "Bewertungen" der Produktseite. |
bloc_accueil | true | Bewertungsblock auf der Startseite. |
display ist immer vollständig, Standardwerte eingeschlossen: Ihr Code muss unsere Standardwerte weder kennen noch übernehmen — ändert sich einmal einer, folgt er von selbst.
accent_color ist null, wenn der Händler keine Farbe gewählt hat oder wenn sein Tarif es nicht mehr erlaubt. Sehen Sie auf Ihrer Seite stets eine Ersatzfarbe vor: genau das tut das Widget, dessen Farbton nur besteht, wenn er hinterlegt ist.
GET
/api/v1/public/health
Ohne Authentifizierung
Verfügbarkeit des Dienstes. Von einer Sonde oder von der Zustandsprüfung eines Moduls abzufragen.
HTTP/1.1 200 OK
{ "status": "ok" }
Bewusst minimal: kein Datenbankzugriff, keine externe Abhängigkeit. Eine träge Datenbank darf keinen falschen Ausfallalarm auslösen — und umgekehrt sagt dieser Endpunkt nichts über den Zustand der Datenbank. Um zu prüfen, ob ein Schlüssel funktioniert, ist /cms/ping aufzurufen.
6. Anzeige-Widgets
Vier HTML-Elemente, die in ein Theme gesetzt werden. Ein einziges Skript zu laden, keine Abhängigkeit, keine Konfiguration: die Adresse der API wird aus der URL des Skripts selbst abgeleitet.
<script src="https://louis.guide/widget/v1/avis.js" async></script>
<avis-score marchand="tissufiesta"></avis-score>
<avis-liste marchand="tissufiesta" par-page="5"></avis-liste>
Das Skript wird einmal pro Seite geladen, wo Sie wollen; die Elemente dürfen davor stehen. Es wird unter /widget/v1/ ausgeliefert: eine unverträgliche Weiterentwicklung erschiene als /v2/, und dieses hier bliebe unverändert bedient — es lebt in Themes, die niemand aktualisieren wird.
Die vier Elemente
| Element | Was es anzeigt | Wohin damit |
<avis-score> |
Durchschnittsnote, Sterne, Anzahl der Bewertungen. |
Produktseite, Shop-Kopfbereich, Seite "Über uns". |
<avis-liste> |
Paginierte Bewertungen, mit Fotos und Antworten des Händlers. |
Reiter "Bewertungen" einer Produktseite, eigene Seite. |
<avis-carrousel> |
Nur Kundenfotos, anklickbar. |
Produktseite, Startseite. |
<avis-flottant> |
In einer Ecke fixiertes Bewertungsabzeichen, anklickbar. |
Die gemeinsame Vorlage, einmal für die ganze Website. |
Attribute
| Attribut | Elemente | Standard | Rolle |
marchand | alle | — |
Pflicht. Öffentlicher Bezeichner des Händlers, derselbe wie auf seiner öffentlichen Seite. |
produit |
Score, Liste, Karussell | — |
Ihr Katalogbezeichner (external_product_id). Fehlt er, bezieht sich das Element auf den ganzen Shop. |
langue | alle | lang der Seite |
Sprache der Beschriftungen. Ersatzweise das lang-Attribut des Dokuments — das das Theme bereits setzt — und danach Französisch. Nur fr und en sind tatsächlich übersetzt; jeder andere Wert fällt auf Französisch zurück, statt halb übersetzte Beschriftungen zu zeigen. |
mini | score | 1 |
Anzahl der Bewertungen, unter der das Element verschwindet. Bei 3 zeigt eine Seite mit nur zwei Bewertungen nichts an, statt einer Note, die auf fast nichts beruht. |
par-page | liste | 5 |
Anzahl der jeweils geladenen Bewertungen; eine Schaltfläche "Mehr anzeigen" lädt den Rest. |
max | carrousel | 12 |
Anzahl der Fotos, höchstens 50. |
Vollständiges Beispiel auf einer Produktseite
<!-- Sous le titre du produit -->
<avis-score marchand="tissufiesta" produit="REF-42" mini="3"></avis-score>
<!-- Photos des acheteurs, sous la galerie du catalogue -->
<avis-carrousel marchand="tissufiesta" produit="REF-42" max="8"></avis-carrousel>
<!-- Dans l'onglet « Avis » -->
<avis-liste marchand="tissufiesta" produit="REF-42" par-page="10"></avis-liste>
<!-- Une seule fois, dans le gabarit commun -->
<avis-flottant marchand="tissufiesta"></avis-flottant>
Ein Widget, das nichts anzeigt, ist nicht zwangsläufig defekt
Drei Situationen lassen ein Element verschwinden, und jedes Mal ist das so gewollt: keine Bewertungen (oder weniger als der Schwellenwert), keine Fotos für das Karussell und jeder Netz- oder Serverfehler.
"Noch keine Bewertungen" auf einer Produktseite anzuzeigen ist schlimmer, als nichts anzuzeigen: der Besucher schließt daraus, dass niemand bestellt hat. Und ein Fehlerbanner im Shop eines Händlers, weil unsere API hustet, wäre nicht zu vertreten — das Element zieht sich aus dem Layout zurück, die Produktseite bleibt unversehrt.
Praktische Folge für den Integrator: bauen Sie kein Layout, das für ein Widget eine feste Höhe reserviert. Es kann überhaupt keinen Platz einnehmen.
Was der Händler ohne Sie steuert
Die Elemente lesen beim Laden /display. Zwei Einstellungen kommen von dort statt aus einem Attribut, und das ist Absicht: der Händler kann seine Tags ein für alle Mal setzen und danach aus seinem Bereich heraus umentscheiden, ohne sein Theme wieder zu öffnen.
-
Das schwebende Abzeichen — an oder aus, rechts oder links, mit seinem Randabstand.
<avis-flottant> in der Vorlage zeigt nichts, solange der Händler es nicht eingeschaltet hat. Es ist standardmäßig aus: bei ihm darf nichts erscheinen, ohne dass er darum gebeten hat.
-
Die Markenfarbe — auf die Flächen angewandt, die es zulassen. Die Sterne behalten ihr Bernstein, ebenso das Grün von "Geprüfter Kauf" und das Bernstein von "Bestritten": diese Farben tragen Bedeutung, sie schmücken nicht, und sie zu übermalen machte die Note bei einem Händler mit blassgelber oder weißer Marke unlesbar.
Die Einstellungen werden nur einmal pro Seite abgefragt, selbst bei vier Elementen: die laufende Anfrage wird geteilt. Das verhindert, dass das Widget zu dem Skript wird, das die Produktseite bremst — ein berechtigter Vorwurf an die meisten Bewertungsmodule.
Abschottung gegenüber dem Theme
Jedes Element rendert seinen Inhalt in einem Shadow DOM: das CSS des Themes greift nicht auf das Widget über, und das des Widgets nicht auf den Shop. Keines von beiden wäre in der anderen Richtung hinnehmbar.
Eine Folge, die man vor dem Versuch kennen sollte: Ihre CSS-Regeln erreichen das Innere der Widgets nicht. Die einzige vorgesehene Anpassung ist die Markenfarbe, eingestellt im Händlerbereich. Eine wirklich maßgeschneiderte Anzeige läuft über die Öffentliche API — genau dafür ist sie dokumentiert.
Bevor Sie irgendetwas einfügen
Unter PrestaShop und WooCommerce setzt das Modul diese Tags selbst, an die richtige Stelle des Themes. Das Einfügen von Hand richtet sich an andere Plattformen und an maßgeschneiderte Themes — Module ansehen.
7. MCP-Server
MCP (Model Context Protocol) stellt dieselben Fähigkeiten bereit wie die API, in einer Form, die ein KI-Assistent selbst entdecken kann. Wo ein Entwickler eine Dokumentation liest, die Authentifizierung schreibt und das JSON auslegt, fragt der Assistent die Liste der Werkzeuge ab, liest ihre Beschreibungen und ruft sie auf.
Konkret: der Händler schließt seinen Assistenten an diesen Server an und schreibt dann "welche Bewertungen haben noch keine Antwort?" oder "antworte auf diese und entschuldige dich für die Verzögerung". Niemand hat Integrationscode geschrieben.
| Wert |
| Adresse | https://louis.guide/api/v1/mcp |
| Transport | JSON-RPC 2.0 über HTTP, per POST |
| Protokollversion | 2024-11-05 |
| Angekündigter Server | avis-clients, Version 1.0.0 |
| Fähigkeiten | tools — weder Ressourcen noch Prompts |
| Authentifizierung |
Bearer-Token (§7.1) oder API-Schlüssel + HMAC-Signatur (§7.2) |
| Tarif | Kostenpflichtig — sonst JSON-RPC-Fehler -32001 |
7.1 Einen Assistenten anschließen: das Token
Das ist der normale Weg und der einzige, der nichts Installiertes verlangt. Der Händler erstellt in seinem Bereich ein Token — Einstellungen · Sammlung, Abschnitt "Einen Assistenten anschließen" — und fügt es dann zusammen mit der Serveradresse in die Konfiguration seines Assistenten ein.
POST https://louis.guide/api/v1/mcp
Authorization: Bearer mcp_live_…
Content-Type: application/json
Übliche Form der Konfigurationsdatei eines MCP-Clients:
{
"mcpServers": {
"avis-clients": {
"url": "https://louis.guide/api/v1/mcp",
"headers": { "Authorization": "Bearer mcp_live_…" }
}
}
}
Prüfung mit einem Befehl, bevor Sie irgendetwas anschließen:
curl -X POST 'https://louis.guide/api/v1/mcp' \
-H 'Authorization: Bearer mcp_live_…' \
-H 'Content-Type: application/json' \
--data-raw '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Was das Token kann und was nicht
| Eigenschaft | Verhalten |
| Geltungsbereich |
Ausschließlich /api/v1/mcp. An der CMS-API vorgelegt, wird es nicht einmal geprüft: keine Übermittlung von Bestellungen, kein Export von Bewertungen, keine Anbindung. |
| Schreiben |
Standardmäßig untersagt. Der Händler setzt bei der Erstellung ausdrücklich das Häkchen "das Verfassen von Antworten erlauben". Ohne dieses erscheint das Werkzeug repondre_a_un_avis nicht einmal in tools/list — der Assistent wird es also nicht vorschlagen. |
| Lebensdauer |
Ein Jahr, danach gilt es nicht mehr. Es ist in zehn Sekunden neu erstellt. |
| Widerruf |
Sofort und endgültig, Token für Token, ohne die API-Schlüssel oder die Module des Händlers anzurühren. |
| Aufbewahrung |
Nur ein einziges Mal angezeigt. Wir behalten davon nichts als einen Fingerabdruck: niemand kann es erneut anzeigen, wir eingeschlossen. |
| Anzahl |
Höchstens drei gültige Token je Konto. |
Ein Bearer-Token reist mit: behandeln Sie es wie ein Passwort
Anders als das HMAC-Geheimnis geht es bei jeder Anfrage hinaus und lebt in der Konfiguration eines Dienstes, den wir nicht kontrollieren. Das ist der Preis des direkten Anschlusses, und deshalb ist es abgegrenzt, ablaufend, widerrufbar und beim Schreiben standardmäßig stumm. Setzen Sie es niemals in eine URL und in kein Code-Repository: URLs landen in den Protokollen aller durchlaufenen Vermittler.
Die Versuche sind auf 20 Fehlschläge je Viertelstunde und IP-Adresse begrenzt — darüber hinaus lautet die Antwort 429.
7.2 Alternative: API-Schlüssel und HMAC-Signatur
Derselbe Endpunkt akzeptiert die in §2 beschriebene Authentifizierung: API-Schlüssel und HMAC-Signatur. Sie hat einen echten Vorteil — das Geheimnis verlässt den Server des Händlers nie — und einen Nachteil, der sie Integratoren vorbehält: kein MCP-Client kann bei jedem Aufruf einen HMAC neu berechnen, sie setzen nur feste Header.
Es braucht also eine Brücke: ein kleines Programm, das vom Assistenten gestartet wird, das JSON-RPC auf seiner Standardeingabe empfängt, es signiert, sendet und die Antwort zurückgibt. Node.js 18 oder neuer, keine Abhängigkeit. Speichern Sie es als pont-mcp.js.
#!/usr/bin/env node
'use strict';
// Pont MCP : entrée standard (JSON-RPC) → API signée → sortie standard.
const crypto = require('crypto');
const readline = require('readline');
const BASE = process.env.LG_API_URL;
const CLE = process.env.LG_API_KEY;
const SECRET = process.env.LG_API_SECRET;
const CHEMIN = '/api/v1/mcp';
readline.createInterface({ input: process.stdin }).on('line', async (ligne) => {
const corps = ligne.trim();
if (!corps) return;
let requete;
try { requete = JSON.parse(corps); } catch (e) { return; }
// Une notification n'a pas d'identifiant, et n'attend AUCUNE réponse :
// en écrire une romprait le protocole côté client.
const attendUneReponse = requete.id !== undefined && requete.id !== null;
const ts = Math.floor(Date.now() / 1000).toString();
// On signe la chaîne qu'on envoie, pas un objet réencodé : réencoder
// produirait tôt ou tard un échappement différent, donc un 401 inexplicable.
const signature = crypto.createHmac('sha256', SECRET)
.update(ts + 'POST' + CHEMIN + corps)
.digest('hex');
try {
const reponse = await fetch(BASE + CHEMIN, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': CLE,
'X-Timestamp': ts,
'X-Signature': 'sha256=' + signature,
},
body: corps,
});
const texte = await reponse.text();
if (attendUneReponse) process.stdout.write(texte + '\n');
} catch (erreur) {
if (attendUneReponse) {
process.stdout.write(JSON.stringify({
jsonrpc: '2.0',
id: requete.id,
error: { code: -32603, message: 'Pont MCP : ' + erreur.message },
}) + '\n');
}
}
});
Eintrag auf Seite des MCP-Clients (übliche Form der Konfigurationsdateien):
{
"mcpServers": {
"avis-clients": {
"command": "node",
"args": ["/chemin/absolu/vers/pont-mcp.js"],
"env": {
"LG_API_URL": "https://louis.guide",
"LG_API_KEY": "ak_live_5c2f81b0",
"LG_API_SECRET": "sk_live_3f9c1a7e5b2d48a6"
}
}
}
}
Das Geheimnis verlässt die Maschine nicht. Es dient dazu, lokal zu signieren; was ins Netz geht, ist die Signatur. Ein absoluter Pfad ist unerlässlich: der Assistent startet das Programm nicht aus dem Ordner, in dem Sie es geschrieben haben.
Um die Brücke zu prüfen, bevor Sie irgendetwas anschließen, geben Sie ihr von Hand eine Zeile. Es sollte eine Liste von Werkzeugen zurückkommen:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| LG_API_URL='https://louis.guide' LG_API_KEY='ak_live_…' LG_API_SECRET='sk_live_…' node pont-mcp.js
7.3 Methoden
| Methode | Wirkung |
initialize |
Kündigt die Protokollversion, die Fähigkeiten und die Identität des Servers an. |
tools/list | Katalog der Werkzeuge und ihrer Eingabeschemata. |
tools/call | Führt ein Werkzeug aus — params.name und params.arguments. |
notifications/initialized, ping | Mit einem leeren Ergebnis quittiert. |
POST /api/v1/mcp
{"jsonrpc":"2.0","id":1,"method":"initialize"}
HTTP/1.1 200 OK
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} },
"serverInfo": { "name": "avis-clients", "version": "1.0.0" }
}
}
7.4 Die drei Werkzeuge
Zwei lesen, eines schreibt. Die Trennlinie ist nicht technisch: Bewertungen zu lesen birgt kein Risiko, während das Veröffentlichen einer Antwort den Händler öffentlich sprechen lässt, auf einer Seite, die wir betreiben — eine unglückliche Formulierung bei einer heiklen Bewertung, und ein Bildschirmfoto macht die Runde.
Deshalb gibt tools/list nur zwei Werkzeuge zurück, wenn der Aufrufer ein Nur-Lese-Token vorlegt. Verdrahten Sie die Liste also nicht fest: fragen Sie sie ab, und kündigen Sie dem Händler nur an, was darin steht.
Werkzeug
lister_avis
Veröffentlichte Bewertungen des Geschäfts, von der neuesten zur ältesten. Das Werkzeug, das der Assistent für "zeig mir die unzufriedenen Kunden" oder "was hat noch keine Antwort?" aufruft.
| Argument | Typ | Standard | Wirkung |
note_max | ganze Zahl 1–5 | — |
Gibt nur Bewertungen zurück, deren Note kleiner oder gleich diesem Wert ist. |
sans_reponse | boolesch | false |
Schließt Bewertungen aus, auf die bereits eine Antwort veröffentlicht wurde. |
limite | ganze Zahl 1–50 | 20 |
Anzahl der gelesenen Bewertungen. |
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "lister_avis",
"arguments": { "note_max": 3, "sans_reponse": true, "limite": 10 }
}
}
Das Ergebnis ist ein Textblock, der JSON enthält — das ist die Form, die das Protokoll für ein strukturiertes Ergebnis vorsieht, und diejenige, die Assistenten zu lesen wissen:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{
"type": "text",
"text": "{\"avis\":[{\"id\":\"9f1c2b3d-…\",\"note\":2,\"titre\":\"Colis abîmé\",\"commentaire\":\"…\",\"langue\":\"fr\",\"auteur\":\"Claire M.\",\"publie_le\":\"2026-08-13\",\"deja_repondu\":false}],\"total\":1}"
}
],
"isError": false
}
}
sans_reponse filtert nach der Grenze, nicht davor. Zwanzig Bewertungen ohne Antwort anzufordern liest die letzten 20 veröffentlichten Bewertungen und entfernt daraus die bereits bearbeiteten: das Ergebnis kann deutlich weniger enthalten, und total sagt das. Erhöhen Sie limite, um das Lesefenster zu weiten.
Hier kommen nur veröffentlichte Bewertungen zurück: weder wartende noch abgelehnte noch zurückgezogene. Für diese ist GET /cms/reviews mit seinem Filter status zuständig.
Werkzeug
resume_reputation
Ein Überblick, ohne Argument. Was der Assistent für "wie steht es um meinen Ruf?" aufruft.
{
"boutique": "Tissu Fiesta",
"note_moyenne": 4.6,
"avis_publies": 318,
"repartition": { "1": 4, "2": 6, "3": 18, "4": 91, "5": 199 },
"avis_produit": 127
}
avis_publies zählt die Bewertungen des Geschäfts; avis_produit zählt gesondert jene zu einem Artikel. Sie zu addieren ergäbe eine Summe, die keiner angezeigten Note entspricht.
Werkzeug
repondre_a_un_avis
Öffentliches Schreiben
| Argument | Typ | Pfl. | Wirkung |
avis_id | Zeichenkette | ja | Bezeichner der Bewertung, wie von lister_avis zurückgegeben. |
contenu | Zeichenkette | ja |
Text der Antwort, bei 3000 Zeichen abgeschnitten. |
{
"avis_id": "9f1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"publiee": false,
"message": "Antwort als Entwurf gespeichert. Der Händler muss sie in seinem Bereich freigeben, bevor sie erscheint."
}
Veröffentlicht oder Entwurf: das hat der Händler entschieden, nicht der Aufruf
Der Händler stellt in seinem Bereich ein, ob von einem Assistenten verfasste Antworten unmittelbar hinausgehen oder auf seine Durchsicht warten. Keines der beiden Verhalten ist absolut richtig: wer zwei Bewertungen pro Woche erhält, will gegenlesen; wer zweihundert erhält, will, dass es hinausgeht.
Diese Einstellung ist kein Parameter der Anfrage, und das ist wesentlich: könnte der Assistent selbst wählen, ob er gegengelesen werden muss, wäre die Garantie nichts mehr wert. Das Feld publiee und das Feld message sagen, was tatsächlich geschehen ist — ein Assistent muss das unverändert an den Händler weitergeben.
Bei einer bereits beantworteten Bewertung wird die Antwort ersetzt. Eine bereits öffentliche Antwort bleibt öffentlich: eine Neufassung schickt sie nie in den Entwurf zurück, was sie ohne Entscheidung von irgendjemandem von der Seite verschwinden ließe.
Was ein Assistent vor dem Verfassen wissen muss
-
In der Sprache der Bewertung antworten — dafür ist das Feld
langue da. Eine französische Antwort unter einer niederländischen Bewertung sagt dem Leser, dass sie nicht gelesen wurde.
-
Niemals ein kaufmännisches Entgegenkommen versprechen, das nicht eingelöst werden kann: Erstattung, Ersatzlieferung, Nachlass. Diese Antwort ist öffentlich und für den Händler verbindlich.
-
Kein Werkzeug ändert oder löscht eine Bewertung, und es wird auch keines geben. Ein Assistent, den man bittet, eine Bewertung "entfernen zu lassen", kann sie nur melden, mit einem zulässigen Grund (§4.6) — die Note ist keiner.
7.5 Fehler
Immer ein HTTP-Status 200, auch im Fehlerfall: bei JSON-RPC reist der Fehler im Body. Ein 4xx ließe den Client glauben, der Transport sei fehlgeschlagen, und die meisten versuchten es erneut, statt die Meldung anzuzeigen.
Die einzige Ausnahme: die Authentifizierung, abgelehnt bevor die JSON-RPC-Schicht erreicht wird. Sie antwortet mit dem gewohnten Fehlerumschlag der API.
| Status | Code | Ursache |
| 401 | invalid_mcp_token |
Token unbekannt, widerrufen oder abgelaufen — absichtlich nicht unterscheidbar. Der Händler legt in seinem Bereich ein neues an. |
| 401 | Codes aus §2 |
HMAC-Weg: Schlüssel fehlt, Signatur oder Zeitstempel abgelehnt. |
| 429 | too_many_attempts |
Mehr als 20 fehlgeschlagene Authentifizierungen in fünfzehn Minuten von derselben Adresse. Warten Sie lieber, als in einer Schleife erneut zu versuchen. |
| Code | Bedeutung | Zu tun |
-32001 |
Der Tarif des Händlers enthält keinen MCP-Zugang. |
Auf einen kostenpflichtigen Tarif wechseln; die Bewertungen bleiben öffentlich lesbar. |
-32601 | Unbekannte JSON-RPC-Methode. | method prüfen. |
-32602 | Unbekanntes Werkzeug. | tools/list aufrufen, die Namen nicht fest verdrahten. |
-32603 |
Fehler der lokalen Brücke — Netz, fehlendes Geheimnis. |
Dieser Code stammt von der obigen Brücke, nicht vom Server. |
Die fachlichen Fehler eines Werkzeugs sind keine JSON-RPC-Fehler: die Antwort bleibt ein Ergebnis, mit isError: true und einem Objekt { "erreur": "…" } im Text. Das gilt für eine nicht gefundene Bewertung, einen leeren Inhalt oder eine mit einem Nur-Lese-Token versuchte Antwort. Der Assistent kann es so dem Händler erklären, statt eine Störung zu vermelden.
8. Eingehende Webhooks
Es gibt keinen ausgehenden Webhook
Die Plattform ruft Sie nicht an: sie sendet keinerlei Benachrichtigung an Ihren Server, wenn eine Bewertung, eine Antwort oder eine Moderationsentscheidung veröffentlicht wird. Um die Aktivität zu verfolgen, fragen Sie GET /api/v1/cms/reviews in Ihrem eigenen Takt ab, gefiltert nach status=published und mit Vergleich von published_at zum letzten Durchlauf.
Ein stündlicher Durchlauf genügt für nahezu jede Anwendung: Bewertungen treffen nicht im Sekundentakt ein, und die Veröffentlichungsrate eines Shops zählt in Einheiten pro Tag. Minütlich abzufragen lässt nichts schneller erscheinen.
Die beiden folgenden Endpunkte bestehen für bestimmte Aufrufer — unseren SMS-Anbieter und unseren Zahlungsdienstleister. Kein Integrator muss sie aufrufen, und keiner kann es: beide sind durch ein Geheimnis verschlossen, das nicht verteilt wird.
POST
/api/v1/stripe/webhook
Stripe-Signatur
Empfängt die Abonnementereignisse: checkout.session.completed, customer.subscription.created, .updated und .deleted. Das ist es, was ein Konto in einen kostenpflichtigen Tarif überführt, und damit das, was die Bewertungs-API und den MCP-Zugang öffnet.
Die Signatur der Nutzlast ist das Einzige, was diese Route schützt: ohne sie könnte jeder "Abonnement aktiv" senden und sich mit einer einzigen curl-Anfrage den kostenpflichtigen Tarif verschaffen. Sie wird vor jedem Lesen des Inhalts geprüft, und ein fehlendes Geheimnis lässt die Anfrage scheitern, statt sie durchzulassen.
Nicht verarbeitete Ereignisse werden mit einem 200 quittiert ({ "ignored": … }): Stripe wertet jede Antwort, die kein 2xx ist, als Fehlschlag und wiederholt drei Tage lang mit wachsenden Abständen. Auf einen Ereignistyp, für den wir keine Verwendung haben, mit 404 zu antworten, verursachte Tausende nutzloser Wiederholungen und dann die Abschaltung des Endpunkts auf deren Seite. Umgekehrt antwortet ein echter Verarbeitungsfehler sehr wohl mit 500 — dort wollen wir, dass Stripe erneut sendet, statt einen Händler, der bezahlt hat, im kostenlosen Tarif zu lassen.
POST
/api/v1/sms/inbound/{token}
Gemeinsames Token
Empfängt die eingehenden SMS, also die "STOP"-Nachrichten. Der Anbieter verarbeitet das Schlüsselwort auf seiner Seite und stellt nicht mehr zu — ohne diesen Endpunkt wüssten wir davon jedoch nichts: wir schickten ihm weiter Nachrichten, die berechnet und nie empfangen werden, der Widerspruch verschwände am Tag eines Anbieterwechsels, und wir könnten nicht nachweisen, ihn beachtet zu haben, obwohl uns die Beweislast trifft.
Das Token reist im Pfad mit, was schwächer ist als eine Signatur — aber es ist das, was die Oberflächen der französischen Anbieter einzurichten wissen. Daher kann dieser Endpunkt nichts anderes, als einen Widerspruch hinzuzufügen: das Schlimmste, was ein betrügerischer Aufruf bewirkt, ist, dass an eine Nummer keine SMS mehr geht. Lästig, nie gefährlich, und vom Backoffice aus umkehrbar.
Das Schlüsselwort wird als erstes Wort der Nachricht gesucht, nicht irgendwo darin: wer "das muss aufhören, dieser Laden ist schlecht" schreibt, bittet nicht um Abmeldung, und ihn dennoch abzumelden nähme ihm den Kanal, über den er rechtmäßig angesprochen wird. Der Widerspruch wird für alle Händler erfasst: die eingehende Nachricht sagt nicht, um welchen Shop es geht — die Person antwortet auf die Absendernummer — und zu raten wäre zugleich falsch und gefährlich.
Er kappt allein den SMS-Kanal. Die E-Mail geht weiter hinaus: sie trägt den Verwaltungslink der Bewertung und die Pflichtangaben, und ein auf einem Kanal geäußerter Widerspruch gilt nicht für den anderen.
9. Ratenbegrenzungen
Die Grenzen werden über ein gleitendes Fenster berechnet: kein Zähler, der zur vollen Stunde auf null springt, und damit kein möglicher Ausbruch zu Beginn eines Zeitraums.
| Kanal | Grenze | Schlüssel | Warum diese Zahl |
Öffentliche API /api/v1/public/ |
60 / Minute |
IP-Adresse |
Eine Produktseite macht zwei Aufrufe: das lässt 30 Seitenaufrufe pro Minute von derselben Adresse zu. Großzügig für einen Besucher, eng für einen Inhaltssauger. |
Verfügbarkeit /public/health |
keine |
— |
Bewusst ausgenommen: er wird von der Überwachung fortlaufend abgefragt, und ihn zu drosseln erzeugte falsche Ausfallmeldungen. |
Anbindung eröffnen POST /pairing |
10 / Stunde |
IP-Adresse |
Jeder Aufruf legt ohne jede Authentifizierung eine Zeile in der Datenbank an. Zehn genügen einem Integrator, der neu ansetzt, reichlich. |
Abfragen POST /pairing/{code} |
240 / 15 Minuten |
IP-Adresse |
Bewusst großzügig: das Modul fragt alle fünf Sekunden ab, während der Händler sein Konto anlegt, seine Adresse bestätigt und zustimmt. |
| MCP-Authentifizierung per Token |
20 Fehlschläge / 15 Minuten |
IP-Adresse |
Zählt ausschließlich die Fehlschläge: eine funktionierende Anbindung rührt daran nie. Stoppt das Durchprobieren anderswo gefundener Token und verhindert, dass ein falsch eingerichteter Client die Protokolle flutet. |
| Abgabe einer Bewertung |
10 / Minute |
Token der Einladung |
Pro Token statt pro IP: mehrere Kunden desselben Unternehmens teilen sich oft eine Ausgangsadresse, und sie gemeinsam zu drosseln bestrafte rechtmäßige Abgaben. |
| Öffentliche Meldung einer Bewertung |
5 / Stunde |
IP-Adresse |
Jedem Leser offen (DSA-Pflicht), also auch jedem Bot. Jede Einsendung legt eine Zeile in der Moderationswarteschlange an. |
Die CMS-API ist nicht gedrosselt, was nicht alles erlaubt
Auf die signierten Endpunkte (/api/v1/cms/ und MCP) wird heute keine Ratenbegrenzung angewandt: sie sind authentifiziert, und das tatsächliche Volumen ist durch das Einladungskontingent des Händlers begrenzt. Behandeln Sie den 429 dennoch — eine Grenze kann hinzukommen, und eine Integration, die sie nicht lesen kann, fällt an dem Tag aus, an dem sie erscheint.
In der Praxis: übermitteln Sie Bestellungen laufend statt in nächtlichen Stapeln von mehreren Tausend, und fragen Sie Bewertungen stündlich statt minütlich ab (§8). Auffälliges Volumen ist auf unserer Seite sichtbar und führt zu einer Kontaktaufnahme, nicht zu einer stillen Abschaltung.
Was eine Überschreitung zurückgibt
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Remaining: 0
{
"error": {
"code": "rate_limit_exceeded",
"message": "Zu viele Anfragen. Versuchen Sie es in wenigen Augenblicken erneut."
}
}
Retry-After gibt die Anzahl der Sekunden an, die zu warten sind. Halten Sie sich daran: sofort erneut zu versuchen verbraucht nur das nächste Fenster. Ein exponentielles Zurückweichen, bei einer Minute gedeckelt, genügt für alle hier beschriebenen Fälle.
Das Abfragen bei der Anbindung bildet die Ausnahme und antwortet { "status": "rate_limited" }: es ist dasselbe Ereignis, ausgedrückt im Vokabular eines Endpunkts, den das Modul in einer Schleife abfragt.
10. Gängige Fehlercodes
Zwei Formate, und meist nur eines zu behandeln
Überall in der API trägt ein Fehler denselben Umschlag:
{
"error": {
"code": "invalid_api_key",
"message": "API-Schlüssel unbekannt, widerrufen oder abgelaufen."
}
}
Der code ist stabil und für Ihr Programm bestimmt; die message ist für den Menschen bestimmt, der Fehler sucht, und kann ohne Ankündigung umformuliert werden. Bauen Sie Ihre Logik niemals auf den Text der Meldung.
Eine einzige Ausnahme: POST /cms/orders, von einer anderen Schicht bedient, gibt seine Validierungsfehler als Liste von violations zurück. Ein robuster Client liest also error.code, sofern vorhanden, und greift sonst auf violations zurück.
HTTP-Status
| Status | Bedeutung | Erneut versuchen? |
| 200 | Erfolg. Bei JSON-RPC steht ein etwaiger Fehler im Body. | — |
| 201 | Angelegt — Bestellung erfasst, Antwort veröffentlicht. | — |
| 202 | Angenommen, aber nicht entschieden: die Meldung reiht sich ein. | — |
| 400 | Unlesbare Anfrage. | Nein, korrigieren. |
| 401 | Schlüssel fehlt, ungültig oder Signatur abgelehnt. | Nein, außer die Uhr ist neu zu synchronisieren. |
| 402 | Der Tarif des Händlers enthält diese Funktion nicht. | Nein. |
| 404 | Unbekannte Ressource — oder außerhalb Ihres Kontos. | Nein. |
| 409 | Konflikt: die Handlung wurde bereits ausgeführt. | Nein, das ist ein Zustand, keine Störung. |
| 415 | Content-Type fehlt oder ist unerwartet. | Nein, senden Sie JSON. |
| 422 | Wohlgeformte, aber abgelehnte Anfrage: fehlendes Feld, Wert außerhalb der Grenzen. | Nein, korrigieren. |
| 429 | Rate überschritten. | Ja, nach Retry-After. |
| 5xx | Störung auf unserer Seite. | Ja, mit wachsender Wartezeit. |
Übersicht der Codes
| Code | Status | Wo | Ursache und Abhilfe |
missing_api_key | 401 | CMS, MCP |
Header X-Api-Key fehlt. |
invalid_api_key | 401 | CMS, MCP |
Schlüssel unbekannt, widerrufen oder abgelaufen — alle drei bewusst nicht unterscheidbar. Prüfen Sie ihn im Händlerbereich. |
missing_signature, missing_timestamp |
401 | CMS, MCP |
Nicht signierter Schreibzugriff. Siehe §2.1. |
invalid_timestamp | 401 | CMS, MCP |
X-Timestamp ist kein Unix-Zeitstempel in Sekunden — meist Millisekunden oder ein ISO-Datum. |
timestamp_out_of_range | 401 | CMS, MCP |
Mehr als 300 s Abweichung. Die Meldung nennt den genauen Wert: synchronisieren Sie die Uhr (NTP). |
signature_mismatch | 401 | CMS, MCP |
Gehen Sie die vier Fallen aus §2.3 der Reihe nach durch. |
invalid_mcp_token | 401 | MCP |
Bearer-Token unbekannt, widerrufen oder abgelaufen — nicht unterscheidbar. Der Händler legt in seinem Bereich einen neuen an (§7.1). |
too_many_attempts | 429 | MCP |
Zu viele fehlgeschlagene Authentifizierungen von derselben Adresse. |
plan_required | 402 | Bewertungen, Antwort |
Funktion ab dem kostenpflichtigen Tarif enthalten. Die öffentliche Anzeige der Bewertungen bleibt kostenlos. |
merchant_not_found | 404 | Öffentliche API |
Unbekannter öffentlicher Bezeichner. Prüfen Sie den Slug, nicht den Handelsnamen. |
review_not_found | 404 | Antwort, Meldung |
Bezeichner unbekannt, fehlerhaft oder zu einem anderen Händler gehörend: die Abschottung verlangt, sie nicht zu unterscheiden. |
already_reported | 409 | Meldung |
Zu dieser Bewertung ist bereits ein Vorgang offen. |
content_required | 422 | Antwort |
content fehlt oder ist nach der Bereinigung leer. |
invalid_reason | 422 | Meldung |
Grund außerhalb der Liste. Eine schlechte Note ist kein zulässiger Grund (§4.6). |
invalid_request | 422 | Anbindung |
shop_domain fehlt oder ist unbrauchbar. |
rate_limit_exceeded | 429 | Öffentliche API |
Siehe §9 und den Header Retry-After. |
-32001 | 200 | MCP |
Tarif ohne MCP-Zugang (ein JSON-RPC-Fehler, kein HTTP-Fehler). |
-32601, -32602 | 200 | MCP |
Unbekannte Methode oder unbekanntes Werkzeug. Gehen Sie über tools/list. |
Drei Symptome, und wo man anfängt
| Symptom | Häufigste Ursache |
| "Gestern lief alles, heute ist alles 401." |
Die Serveruhr ist abgedriftet. GET /cms/ping gibt server_time zurück: vergleichen Sie es mit Ihrer, bevor Sie anderswo suchen. |
| "Der Ping geht durch, aber alle meine Schreibzugriffe scheitern." |
Der Schlüssel ist gut, die Signatur nicht — genau das lässt diese geteilte Regelung schließen. Der Body wurde fast immer nach dem Signieren neu kodiert (§2.3). |
| "Das Widget zeigt nichts, aber die API antwortet in der Konsole mit 200." |
Domain auf Händlerseite nicht hinterlegt: der Browser blockiert das Lesen mangels CORS-Header. Oder schlicht: es gibt noch keine Bewertungen — ein leeres Widget nimmt sich selbst von der Seite (§6). |
Wenn nichts davon passt
Schreiben Sie uns aus dem Händlerbereich und fügen Sie drei Dinge bei: den aufgerufenen Pfad, den Zeitstempel der Anfrage und den erhaltenen Fehlercode. Mit diesen dreien lässt sich die Anfrage in den Protokollen finden; ohne sie bleibt als einzige Antwort, Sie danach zu fragen.
↑ Zurück zum Anfang