Lesende REST-API für Partner und Kunden
Stand 2026-09-23, Entwurf aus der Discovery-Session Partner-API (App). Gilt für die Kunden-App (app/api). Änderungen als datierter Nachtrag in der Änderungshistorie am Ende, nie stillschweigend. Grundlage: docs/CONCEPT.md (Nachtrag 2026-09-23), docs/SCHNITTSTELLE-CLOUD.md (Teil 4, Läufe und Wizard-Leitplanke). Beispieldateien zu jedem Endpunkt liegen in docs/api-examples/ und stammen vom erfundenen Kunden „Hotel Seeblick"; Zahlen, Marken und Domains dort sind ausgedacht, die Summen gehen auf.
Verfügbarkeit in Stufen. Die Doku ist vor der Software fertig, damit Partner früh integrieren können. Jeder Endpunkt trägt seine Stufe:
| Stufe | Bedeutung |
|---|---|
| 1 | Nur Doku und Beispieldateien. |
| 2 | Endpunkt antwortet in Produktion mit den Beispieldaten für Testkeys (vm_test_…). |
| 3 | Endpunkt liefert echte Daten für Produktivkeys (vm_live_…). |
Stand 2026-09-23 stehen alle Endpunkte auf Stufe 1. Die Tabelle in Abschnitt 4 wird bei jedem Deploy nachgeführt.
1. Zweck und Abgrenzung #
Die API liefert die Ergebnisse der Verankerungs-Messung maschinenlesbar an zwei Arten von Abnehmern:
- Partner (Whitelabel): eine Organisation, die eigene Kunden bei uns anlegt und deren Ergebnisse in ihrem eigenen Dashboard zeigt. Beispiel: Hotels im Kunden-Dashboard eines Partners.
- Direktkunden: ein Kunde der Kunden-App, der seine eigenen Daten zusätzlich per API abruft.
Die API ist lesend, mit einer Ausnahme: ein Partner kann einen Kunden anfragen (Abschnitt 4.4). Alles Weitere bleibt Arbeit des Operators: Wizard und Erstmessung in der Cloud, Paketwechsel, Pausieren, Beenden. Die Messung selbst läuft im getrennten Mess-System; die App speichert jede Messung als unveränderlichen Snapshot und liefert daraus.
Nicht Teil der API: Kosten und Preise, Nutzerverwaltung, Inbox, manuelle Nachmessung, Ändern oder Löschen von Kunden, Ändern von Prompts, Rohdaten des Mess-Systems über die Läufe hinaus (Abschnitt 4.7).
2. Grundlagen #
2.1 Basis-URL und Version #
https://app.verankerungsmessung.de/api/v1
Die Hauptversion steht im Pfad. Innerhalb von v1 sind Änderungen additiv: neue Felder, neue Endpunkte, neue Werte in Listen. Abnehmer müssen unbekannte Felder tolerieren. Ein Feld wird nie entfernt, umbenannt oder in seiner Bedeutung geändert, ohne dass eine v2 entsteht. Jede Antwort trägt den Header X-VM-API-Version: 1.0 (Minor zählt additive Änderungen).
2.2 Authentifizierung #
Jeder Aufruf trägt einen API-Key:
Authorization: Bearer vm_live_3f9c…
- Keys legt der Operator an und übergibt sie einmalig; die App speichert nur einen Hash. Ein Key kann jederzeit widerrufen werden.
- Präfix
vm_live_für Produktivdaten,vm_test_für die Beispieldaten ausdocs/api-examples/(Abschnitt 7). - Jeder Key hat einen Geltungsbereich (
scope):partnersieht alle Kunden des Partners,customersieht genau einen Kunden. Die Endpunkte sind für beide gleich; nur der sichtbare Datenraum unterscheidet sich. - Fehlender oder ungültiger Key:
401. Key gültig, Ressource gehört einem anderen Datenraum:404(kein403, damit die Existenz fremder IDs nicht erkennbar ist).403gibt es nur für Aktionen, die der Geltungsbereich nicht erlaubt, etwaPOST /customersmit einemcustomer-Key. - Nur HTTPS. Keys gehören auf den Server des Abnehmers, nie in einen Browser.
2.3 Format #
- JSON, UTF-8, Feldnamen in
camelCaseund Englisch. Texte in der Sprache der Messung bzw. des Reports (Abschnitt 3.7). - Zeitstempel ISO 8601 in UTC mit
Z(2026-10-03T06:15:00Z), Datumswerte alsYYYY-MM-DD. - Optionale Werte sind explizit
null, nie fehlende Schlüssel. - Zählwerte sind absolute Läufe (Integer). Anteile (
share) sind Dezimalzahlen mit 3 Nachkommastellen, nur für die Zielmarke; für andere Marken rechnet der Abnehmerruns / n. - IDs: Kunden, Messungen und Tech-Checks tragen UUIDs; Prompts und Läufe Integer-IDs, die über alle Messungen eines Kunden stabil sind. Ein Prompt behält seine ID von der Erstmessung bis zur letzten Messung, das ist der Schlüssel für Zeitreihen.
2.4 Fehler #
{ "error": "Messung nicht gefunden", "code": "not_found", "detail": null }
| HTTP | code | Bedeutung |
|---|---|---|
| 401 | unauthorized | Key fehlt oder ist ungültig oder widerrufen |
| 403 | forbidden | Aktion für diesen Geltungsbereich nicht erlaubt |
| 404 | not_found | Ressource unbekannt oder nicht im Datenraum des Keys |
| 409 | conflict | Aktion derzeit nicht möglich, Grund in detail |
| 422 | validation_failed | Body oder Parameter ungültig, Grund in detail |
| 429 | rate_limited | Limit überschritten, Header Retry-After in Sekunden |
| 500 | internal_error | Fehler bei uns; bitte mit Zeitpunkt melden |
error ist ein deutscher Text für Menschen und kann sich ändern; code ist stabil und für Programme.
2.5 Rate-Limit #
60 Anfragen je Minute und Key. Jede Antwort trägt X-RateLimit-Limit, X-RateLimit-Remaining und bei 429 zusätzlich Retry-After. Eine Messung ändert sich nach Freigabe nie; ein Abnehmer holt sie einmal und speichert sie. Für „gibt es Neues" reicht ein täglicher Aufruf der Messungsliste.
2.6 Listen und Seiten #
Listen liefern { "items": [ … ], "nextCursor": string | null }. Parameter limit (Standard 50, Höchstwert 200) und cursor (Wert aus nextCursor). Ist nextCursor null, gibt es keine weitere Seite. Sortierung je Endpunkt angegeben und stabil.
3. Begriffe #
3.1 Kunde und Paket #
Ein Kunde ist eine Marke mit einer Domain in einem Markt. Bei einem Partner ist das dessen Endkunde, etwa ein Hotel. Der Kunde hat ein Paket: die Zahl der gemessenen Prompts (10, 20, 30 oder 40) und das Intervall (monthly oder quarterly). Das Paket bestimmt den Preis; Abrechnung läuft außerhalb der API.
Kundenstatus:
status | Bedeutung |
|---|---|
requested | angefragt; der Operator bereitet den Wizard und die Erstmessung vor |
active | Erstmessung freigegeben, Messungen laufen im Intervall des Pakets |
paused | vom Operator pausiert, keine neuen Messungen, alte bleiben abrufbar |
ended | beendet, Daten bleiben lesbar |
3.2 Messung, Prompt, Plattform, Lauf #
Eine Messung ist ein Durchlauf aller Prompts des Kunden auf allen Plattformen zu einem Zeitpunkt. Ein Prompt ist eine wörtliche Frage, wie ein Nutzer sie einer KI stellt. Eine Plattform ist ein KI-Assistent (chatgpt, gemini). Ein Lauf ist eine einzelne Antwort einer Plattform auf einen Prompt; je Prompt und Plattform gibt es n Läufe (Standard 50), damit Häufigkeiten belastbar sind.
3.3 Zielmarke und Kategorien #
Die Zielmarke ist die Marke des Kunden. Jede Antwort wird ausgewertet, welche Marken sie nennt und wie:
| Kategorie | Bedeutung |
|---|---|
recommended | die Marke wird als Empfehlung genannt, also als Antwort auf die Frage |
mentioned | die Marke kommt in irgendeiner Form vor; enthält recommended und negative |
negative | die Marke wird mit einem Einwand oder einer Abwertung genannt |
Je Marke, Prompt und Plattform zählt die API, in wie vielen der n Läufe das zutrifft. Ein Lauf zählt je Marke und Kategorie höchstens einmal. Für einen einzelnen Lauf gilt die Einordnung der Zielmarke targetClassification mit Vorrang recommended vor negative vor mentioned, sonst none.
3.4 Quellen und Klassen #
Zitiert eine Antwort Webseiten, zählt die API je Prompt und Plattform, wie viele Läufe eine Domain zitieren (citingRuns), und darunter die einzelnen URLs. Jede Domain trägt eine Klasse:
kind | Bedeutung |
|---|---|
own | Domain der Zielmarke |
competitor | Domain einer anderen erkannten Marke; brand nennt sie |
third | Dritte: Portale, Presse, Verzeichnisse, Karten |
3.5 Ampel und Schwellen #
Die Ampel ist unsere Bewertung des Anteils empfohlener Läufe der Zielmarke: gruen ab thresholds.ampel.gruen, gelb ab thresholds.ampel.gelb, sonst rot. Die Schwellen stehen in jeder Messung; ein Abnehmer kann sie übernehmen oder eigene Regeln auf die Zählwerte anwenden.
3.6 Stufen und Arten von Prompts #
Das Raster einer Vollmessung hat Stufen 0 bis 3, von der breiten Frage zur spitzen Nische. Jeder Prompt trägt stage.level und ein Label in der Reportsprache. kind unterscheidet:
kind | Bedeutung |
|---|---|
question | Kundenfrage entlang einer Achse (Anlass, Budget, Lage …) |
vocabulary | breite Frage mit einem Suchbegriff, Stufe 0 |
attribute | Frage nach einer Eigenschaft der Zielmarke, attribute gesetzt |
3.7 Sprachen #
market.languageCode ist die Messsprache der Prompts und Antworten. market.reportLanguage (de oder en) ist die Sprache der redaktionellen Texte und Labels im Block report. Marken- und Domain-Namen sind sprachneutral. Aufzählungswerte (ampel, kind, classification, verdict, horizon, status) sind Schlüssel und werden nie übersetzt.
4. Endpunkte #
| Endpunkt | Zweck | Stufe |
|---|---|---|
GET /me | Key prüfen, Geltungsbereich, Limits | 1 |
GET /customers | eigene Kunden | 1 |
GET /customers/:id | ein Kunde | 1 |
POST /customers | Kunde anfragen (nur partner) | 1 |
GET /customers/:id/measurements | freigegebene Messungen | 1 |
GET /measurements/:id | eine Messung vollständig | 1 |
GET /measurements/:id/runs | alle Läufe einer Messung | 1 |
GET /customers/:id/tech-checks | Tech-Check-Historie | 1 |
GET /tech-checks/:id | ein Tech-Check | 1 |
POST /customers/:id/tech-checks | Tech-Check auslösen | 1 |
4.1 GET /me #
Prüft den Key und zeigt, was er sieht. Beispiel api-examples/me.json.
| Feld | Typ | Bedeutung |
|---|---|---|
key.id, key.label | string | Kennung und Bezeichnung des Keys, wie vom Operator vergeben |
key.scope | partner | customer | Geltungsbereich |
key.createdAt, key.lastUsedAt | timestamp | null | |
partner | object | null | bei scope = partner: id, name, packages[] (wählbare Pakete) |
customer | object | null | bei scope = customer: der Kunde wie in 4.3 |
rateLimit | { limit, window } | |
apiVersion | string | wie Header X-VM-API-Version |
4.2 GET /customers #
Alle Kunden im Datenraum des Keys, sortiert nach createdAt absteigend. Bei scope = customer genau ein Eintrag. Parameter status filtert auf einen Wert aus 3.1. Beispiel api-examples/customers.list.json.
4.3 GET /customers/:id #
Beispiel api-examples/customers.get.json.
| Feld | Typ | Bedeutung |
|---|---|---|
id | uuid | |
partnerId | uuid | null | null bei Direktkunden |
name | string | Firmierung, wie vom Partner angegeben |
brand | string | null | Zielmarke; wird vom Operator im Wizard festgelegt, bis dahin null |
domain | string | Hostname ohne Protokoll |
status | enum | 3.1 |
package | { prompts, interval } | prompts ∈ 10, 20, 30, 40; interval ∈ monthly, quarterly |
market | { locationCode, languageCode, reportLanguage } | null | DataForSEO-Standort und -Sprache der Prompts, Reportsprache; null bis zur Erstmessung |
notes | string | null | Briefing des Partners aus der Anfrage, unverändert |
createdAt | timestamp | Anfrage |
activatedAt | timestamp | null | Freigabe der Erstmessung |
nextMeasurementAt | date | null | geplanter Tag der nächsten Messung |
latestMeasurement | { id, label, releasedAt } | null | neueste freigegebene Messung |
latestTechCheck | { id, checkedAt } | null |
4.4 POST /customers #
Nur scope = partner. Legt einen Kunden mit status = requested an. Der Operator bekommt eine Mail, führt den Wizard im Mess-System durch, startet die Erstmessung und gibt sie frei; ab dann ist der Kunde active. Der Partner sieht den Fortschritt über GET /customers/:id. Abrechnung des Pakets beginnt mit der Anfrage, außerhalb der API.
Body, Beispiel api-examples/customers.create.request.json:
| Feld | Pflicht | Regeln |
|---|---|---|
name | ja | 2 bis 120 Zeichen |
domain | ja | Hostname, ohne Protokoll und Pfad; wird auf Kleinschreibung und ohne www. normalisiert |
package.prompts | ja | 10, 20, 30 oder 40, muss zu einem Paket des Partners passen (GET /me) |
package.interval | ja | monthly oder quarterly, muss zum Paket passen |
notes | nein | bis 4.000 Zeichen; was für die Messung wichtig ist: Zielgruppen, Anlass, Wettbewerber, Sprache der Gäste |
Antworten: 201 mit dem Kunden wie in 4.3 (api-examples/customers.create.response.json); 409 conflict, wenn für diese Domain bereits ein Kunde des Partners existiert, der nicht ended ist; 422 validation_failed.
Kein PATCH, kein DELETE. Paketwechsel, Pausieren und Beenden laufen über den Operator.
4.5 GET /customers/:id/measurements #
Freigegebene Messungen des Kunden, neueste zuerst. Entwürfe und laufende Messungen erscheinen nicht. Beispiel api-examples/measurements.list.json.
| Feld | Typ | Bedeutung |
|---|---|---|
id, customerId | uuid | |
label | string | Bezeichnung des Operators, etwa Messung 2026-Q4 |
sequence | int | laufende Nummer je Kunde, Erstmessung = 1 |
periodStart, periodEnd | date | null | Erhebungszeitraum |
releasedAt | timestamp | Freigabe; ab hier unveränderlich |
platforms | string[] | Plattform-Schlüssel |
n | int | Läufe je Prompt und Plattform |
promptCount | int | gemessene Prompts |
runsAvailable | bool | ob 4.7 Läufe liefert |
techCheckId | uuid | null | Tech-Check, der mit dieser Messung lief |
overview[pf] | { recommended, n, share } | Summe empfohlener Läufe der Zielmarke über alle Prompts, n = Prompts × Läufe |
4.6 GET /measurements/:id #
Die Messung vollständig. Beispiel api-examples/measurements.get.json (etwa 120 KB bei 10 Prompts). Struktur: Kopf, prompts[] mit Frage und Ergebnissen je Plattform, report mit den redaktionellen Blöcken.
Kopf
| Feld | Typ | Bedeutung |
|---|---|---|
id, customerId, label, sequence, periodStart, periodEnd, releasedAt, n | wie 4.5 | |
platforms | { key, label }[] | Reihenfolge wie in den Ergebnissen |
market | object | 4.3 |
target | { brand, domain } | Zielmarke |
thresholds | object | Schwellen unserer Bewertung, 3.5 |
prompts | array | siehe unten, sortiert nach id |
report | object | siehe unten |
runs | { available, count, href } | Verweis auf 4.7 |
techCheckId | uuid | null |
prompts[]
| Feld | Typ | Bedeutung |
|---|---|---|
id | int | stabil über alle Messungen des Kunden |
kind | enum | 3.6 |
label | string | Kurzlabel in der Reportsprache |
text | string | die wörtliche Frage in der Messsprache; das ist der Prompt |
stage | { level, label } | Stufe 0 bis 3 |
axis | { key, label } | null | Achse, null bei Attribut-Fragen |
attribute | { slot, label } | null | nur bei kind = attribute |
results[pf] | object | ein Eintrag je Plattform, siehe unten; das ist das Ergebnis |
results[pf]
| Feld | Typ | Bedeutung |
|---|---|---|
n | int | ausgewertete Läufe; 0, wenn die Plattform für diesen Prompt keine Läufe hat |
target | { recommended, mentioned, negative, share, ampel } | Zielmarke; share = recommended / n |
brands[] | { name, isTarget, recommended, mentioned, negative, n } | alle erkannten Marken; Zielmarke immer an erster Stelle, auch mit Nullen; danach absteigend nach recommended, mentioned, Name |
sources[] | { domain, kind, brand, citingRuns, n, urls[] } | alle zitierten Domains, absteigend nach citingRuns; urls[] = { url, title, citingRuns }, bereinigt um Tracking-Parameter |
examples[] | { classification, text } | bis zu 3 Antworttexte, deterministisch gewählt: je eine je vorhandener Klassifikation in der Reihenfolge recommended, negative, mentioned, none, dann aufgefüllt; Markdown wie von der Plattform geliefert |
Konsistenz: brands[0].recommended === target.recommended, jede Zahl ≤ n, recommended ≤ mentioned, negative ≤ mentioned, jede urls[].citingRuns ≤ citingRuns.
report
Die redaktionellen Blöcke der Vollmessung, Struktur identisch mit dem Verankerungs-Report. Alle Texte in market.reportLanguage. Zählwerte darin sind dieselben wie in prompts[], nur ausgewählt und angereichert.
| Block | Inhalt |
|---|---|
content | keyMessage (Kernbotschaft), notTargeted (bewusst nicht gemessen), methodNote, actions[] (title, desc, horizon ∈ schnell wirksam | nachhaltig, horizonHint, target), kompakt.findings[] (title, text) |
funnel[] | Trichter über die Stufen: je Prompt byPlatform[pf] = { hits, n } der Zielmarke und competitors[pf][] Top 3 |
baseline[pf] | Vergleichswert der breiten Frage { hits, n, promptId } |
attributes[] | Attribut-Fragen mit klass ∈ driver | mid | barrier je Plattform, relativ zur Baseline mit thresholds.driverFactor und barrierFactor |
targetSegments[] | Soll-Ist je Zielsegment: label, rationale, verdict ∈ erfuellt | weitgehend | gespalten | luecke, finding, cells[] |
radar[] | Verankerungsprofil je Segment { label, byPlatform } |
landkarte | columns[] Marken der Peer-Gruppe, rows[] je Prompt mit values[pf][brand] = { hits, n } und winner[pf] |
vokabular[] | Stufe-0-Begriffe mit Zielmarke und stärkstem Wettbewerber |
sources[pf][] | Top 8 Domains je Plattform über alle Prompts, citingRuns von totalRuns, mit kind, brand und redaktioneller note (carries, supports, outdated, je Text oder null) |
hits in den Report-Blöcken bedeutet immer empfohlene Läufe der genannten Marke.
4.7 GET /measurements/:id/runs #
Alle einzelnen Antworten der Messung, für eigene Auswertung und Anonymisierung. Parameter: promptId (int), platform (string), cursor, limit (Standard 50, Höchstwert 200). Sortierung nach id aufsteigend. Beispiel api-examples/measurements.runs.json zeigt eine Seite mit promptId = 403; der Testkey liefert je Prompt und Plattform 3 Läufe, in Produktion sind es n.
409 conflict, wenn runsAvailable false ist (Messungen vor der Einführung dieses Endpunkts).
| Feld | Typ | Bedeutung |
|---|---|---|
measurementId | uuid | |
filter | { promptId, platform } | die angewandten Filter oder null |
total | int | Läufe, die dem Filter entsprechen |
items[].id | int | Lauf-ID, stabil |
items[].promptId, items[].platform | Schlüssel in prompts[] und platforms[] | |
items[].completedAt | timestamp | Zeitpunkt der Antwort |
items[].modelHint | string | null | Modellangabe der Plattform, wörtlich, oft null |
items[].targetClassification | enum | Einordnung der Zielmarke in diesem Lauf, 3.3 |
items[].text | string | die vollständige Antwort als Markdown, ungekürzt |
items[].mentions[] | { verbatim, brand, isTarget, category, evidence } | jede erkannte Markennennung: verbatim = Schreibweise im Text, brand = kanonischer Name oder null, wenn die Zuordnung offen ist, evidence = Textstelle, auf die sich die Einordnung stützt |
items[].citations[] | { url, domain, kind } | zitierte Seiten des Laufs |
4.8 Tech-Check #
Der Tech-Check prüft, ob die Website des Kunden für KI-Assistenten technisch erreichbar und lesbar ist. Er läuft automatisch mit jeder Messung und auf Anforderung, einmal je Kunde und Tag. Geprüft werden die Startseite, robots.txt, sitemap.xml und llms.txt; keine weiteren Seiten. Ergebnisse sind unveränderliche Snapshots mit Historie.
GET /customers/:id/tech-checks listet die Snapshots, neueste zuerst (api-examples/tech-checks.list.json). GET /tech-checks/:id liefert einen Snapshot (api-examples/tech-checks.get.json). POST /customers/:id/tech-checks ohne Body startet einen neuen Check und antwortet 202 mit status = queued und href (api-examples/tech-checks.create.response.json); 409 conflict, wenn heute schon einer lief. Der Snapshot ist nach wenigen Minuten unter href abrufbar, bis dahin 404.
| Feld | Typ | Bedeutung |
|---|---|---|
id, customerId | uuid | |
measurementId | uuid | null | gesetzt, wenn der Check mit einer Messung lief |
trigger | measurement | api | admin | Auslöser |
checkedAt | timestamp | |
url, finalUrl | string | Startseite wie angefragt und nach Weiterleitungen |
summary | { ok, warn, fail, info } | Anzahl Checks je Status |
checks[] | { key, status, value, detail } | siehe unten |
lighthouse | { source, fetchedAt, mobile, desktop } | Kennzahlen aus der PageSpeed Insights API, je Gerät performance, accessibility, bestPractices, seo (0 bis 100), fcpMs, lcpMs, tbtMs, cls, speedIndexMs; null, wenn die Abfrage scheiterte |
status je Check: ok unauffällig, warn sollte behoben werden, fail verhindert oder beschränkt die Nutzung durch KI-Assistenten, info Feststellung ohne Wertung. Die Wertung ist unsere; ein Abnehmer kann value eigenständig interpretieren.
key | value | Wertung |
|---|---|---|
http_status | int | ok bei 200, sonst fail |
redirect_chain | string[] | ok bis 2 Weiterleitungen, sonst warn |
tls | { validUntil, issuer } | fail bei ungültigem Zertifikat |
robots_txt | URL | null | warn, wenn fehlend |
robots_ai_crawler:<Name> | allow | disallow | partial | je Crawler eine Zeile: GPTBot, OAI-SearchBot, ChatGPT-User, Google-Extended, Googlebot, PerplexityBot, ClaudeBot, Bytespider, CCBot, Applebot-Extended. fail bei disallow für Crawler, die Antworten speisen (GPTBot, OAI-SearchBot, ChatGPT-User, Google-Extended, Googlebot, PerplexityBot, ClaudeBot); info bei den übrigen |
sitemap_xml | URL | null | warn, wenn fehlend |
llms_txt | URL | null | warn, wenn fehlend |
canonical | URL | null | warn, wenn fehlend oder auf fremde Seite |
meta_robots | string | null | fail bei noindex |
json_ld | string[] Typen | warn, wenn kein branchentypischer Typ |
raw_text_words | int | Wörter sichtbarer Text im HTML ohne JavaScript; warn unter 200, fail unter 50 |
js_rendering_hint | { frameworks[], emptyRoot, scriptShare } | Heuristik ohne Browser: fail bei leerem App-Container, warn bei hohem Skriptanteil; ausdrücklich ein Hinweis, keine Rendering-Prüfung |
5. Hinweise für Anonymisierung und Weiterverarbeitung #
Prompt und Ergebnis liegen getrennt vor: der Prompt ist prompts[].text, das Ergebnis prompts[].results[pf]. Wer Ergebnisse ohne Marken- oder Ortsnamen zeigen will, muss diese Stellen behandeln:
- Strukturierte Markennamen:
results[pf].brands[].name,results[pf].sources[].brand,report.landkarte.columns[]und die Schlüssel inreport.landkarte.rows[].values[pf],report.funnel[].competitors[pf][].name,report.vokabular[].topCompetitor[pf].name,report.sources[pf][].brand,runs.items[].mentions[].brand. Die Liste aller Marken einer Messung ist die Vereinigung vonbrands[].nameüber alle Prompts und Plattformen; daraus lässt sich eine Ersetzungstabelle bauen. - Freitexte mit Marken- und Ortsnamen:
prompts[].text,prompts[].label,results[pf].examples[].text,runs.items[].text,runs.items[].mentions[].verbatimundevidence, alle Texte inreport.content,report.targetSegments[],report.sources[pf][].noteundurls[].title.mentions[].verbatimnennt die exakte Schreibweise im Text und hilft beim Ersetzen; Ortsnamen erkennt die API nicht. - Domains und URLs in
sources[],citations[]undreport.sourcesverraten Marken (kind = ownodercompetitor). - Antworttexte sind Markdown der Plattform, mit Fettdruck, Listen und gelegentlich Zitatmarken; Gemini-Antworten enthalten teils Fußnotenzeichen.
6. Geplant, nicht Teil von 1.0 #
Wird bei Umsetzung als Nachtrag ergänzt, additiv:
- Webhook „Messung freigegeben" und „Tech-Check fertig" an eine URL des Abnehmers, signiert mit HMAC nach dem Muster unseres Cloud-Webhooks.
- Prompts ergänzen oder stilllegen je Kunde.
- Verwaltung der API-Keys durch Partner und Kunden selbst.
- Delta-Endpunkt zwischen zwei Messungen; bis dahin rechnet der Abnehmer über die stabilen
promptIds. - Englische Fassung dieser Doku.
- Tech-Check: Rendering-Prüfung mit echtem Browser, weitere Seiten.
7. Testzugang #
Ein Testkey (vm_test_…) liefert ab Stufe 2 auf allen Endpunkten die Beispieldaten aus docs/api-examples/: Partner „Beispiel-Partner GmbH" mit dem Kunden „Hotel Seeblick AG" (active, 2 Messungen, 2 Tech-Checks) und einem zweiten Kunden im Status requested. POST /customers und POST /customers/:id/tech-checks antworten mit den Beispielantworten, ohne etwas zu speichern. Die IDs der Beispieldateien sind fest, damit Integrationstests darauf verweisen können. Rate-Limit und Fehlerformat sind wie in Produktion.
Änderungshistorie #
- 2026-09-23 — Entwurf 1.0 aus der Discovery-Session Partner-API (App). Alle Endpunkte Stufe 1.