Integrationen
CI/CD mit der API
Ein Deployment, das die Messung zerstört, fängt man am besten ab, bevor es in Produktion geht. Die API spielt Ihre gespeicherten Journeys gegen die Vorschau einer konkreten Änderung ab, vergleicht das Ergebnis mit dem erwarteten Zustand und liefert ein eindeutiges „bestanden“ oder „nicht bestanden“, an dem der CI-Job endet. Diese Anleitung zeigt, wie Sie diesen Ablauf aufbauen und wo er üblicherweise hängen bleibt.
Der Ablauf hat vier Schritte: starten, warten, das Gate auswerten und bei Bedarf SARIF oder einen Kommentar ausgeben.
API-Schlüssel, Abfragen und der Export der Belege gibt es ab Starter; Journeys gegen Vorschau-URLs abzuspielen erfordert Professional.
GitHub App oder eigene Aufrufe
Sie haben zwei Möglichkeiten, beide auf derselben API:
- Die GitHub App (Pull-Request-Prüfung) richten Sie im Dashboard ein; ins Repository kommt nichts. Ihre Regel ist fest: Sie meldet nur direkte Änderungen im Data Layer der Website (
window.dataLayer,window.adobeDataLayer) und blockiert nur kritische Befunde. - Eigene API-Aufrufe funktionieren in jeder CI, und die Gate-Regel bestimmen Sie selbst. Warten und Auswertung enthalten die CI-Rezepte für GitHub, GitLab und Bitbucket bereits.
Reicht Ihnen die Standardregel auf GitHub? Nehmen Sie die App. Eine andere CI oder eine andere Regel heißt API.
Der Schlüssel
Den Schlüssel legt ein Editor oder eine höhere Rolle unter Projekteinstellungen › API-Schlüssel an. Ein Schlüssel hat eine Berechtigung: Lesen, Bearbeiten oder Admin. CI löst Scans aus und liest sie, braucht also Bearbeiten. Mehr braucht der Schlüssel nicht. Der Ablauf ist optional, und den vollständigen Schlüssel sehen Sie nur einmal, direkt nach dem Anlegen.
Der Schlüssel beginnt mit ap_live_ und gehört in einen Header Authorization: Bearer … oder X-API-Key: …. Speichern Sie ihn als maskierte Variable oder Secret, nie im Repository. Ein Schlüssel funktioniert ab dem Starter-Tarif. Ein Aufruf, den der Tarif nicht enthält, bekommt 403 plan_required, und error.meta.minTier nennt den nötigen Tarif: Ein Vorschau-Lauf, um den es in dieser Anleitung geht, braucht Professional. Fällt ein Projekt auf Free zurück, bleiben seine Schlüssel gespeichert und funktionieren nach einem Upgrade wieder. Details stehen in der API-Referenz.
Start gegen eine Vorschau
Einen Scan starten Sie mit POST /api/v1/targets/{id}/scans; die ID des Tests finden Sie über GET /api/v1/targets. Enthält der Body eine url mit der Vorschau-Adresse, läuft der Scan als Dry-Run: Er erzeugt Abweichungen und ein Ergebnis für das Gate, in die Journey wird aber nichts zurückgeschrieben. Erwarteter Zustand, überwachte Tools und Regressionsverlauf bleiben unverändert. Ohne url startet derselbe Aufruf einen normalen Scan des gespeicherten Tests.
Bei einem Website-Test reicht eine url allein für eine Journey nicht. Ohne Scan-Typ startet die Anfrage den Standard-Scan des Tests, ein Compliance-Audit der Vorschau. Um eine Journey abzuspielen, senden Sie "scanType": "web_journey" und optional eine journeyId; ohne sie wird die erste Journey des Tests genommen, die Ihr Tarif ausführen lässt. Die Antwort enthält dann einen Scan: seine id und pollUrl.
Mit "journeys": "all" werden alle Journeys des Tests abgespielt, die Ihr Tarif ausführen lässt, und maxJourneys begrenzt ihre Anzahl. Die Antwort hat dann eine andere Form: Es gibt keine id auf oberster Ebene, nur ein Feld scans mit id und pollUrl je Journey und ein Feld failed mit Journeys, die sich nicht starten ließen, jeweils mit error.code. Ihr Skript muss auf jede scans[].pollUrl warten. Ein nicht leeres failed sollte den Job scheitern lassen.
Zwei Dinge lohnt es sich gleich einzustellen:
- Einen Header
Idempotency-Key. Ein erneuter Versuch mit demselben Schlüssel startet keinen zweiten Scan, sondern liefert den ersten. Der Schlüssel steht für genau eine Anfrage: den Commit, den CI-Lauf (auf GitHub den Run und seinen Versuch, auf GitLab den Job), den Test, die Auswahl der Journeys und die Vorschau-URL. Lassen Sie ihn bei Wiederholungen dieser Anfrage unverändert. Solange die erste Anfrage noch verarbeitet wird, bekommt eine Wiederholung409 idempotent_in_progressmitRetry-After; warten Sie und senden Sie sie erneut. Ein abgeschlossener Schlüssel liefert dieselben Scans, solange sie existieren, auch nach 24 Stunden, und die Vorschau-URL bleibt meist für den ganzen Pull Request gleich, also muss sich der Schlüssel mit jedem neuen Push ändern; deshalb gehört der Commit hinein. Der CI-Lauf sorgt zusätzlich dafür, dass ein erneut gestarteter Job einen frischen Scan bekommt. Ohne die URL bekäme derselbe Commit auf einer zweiten Vorschau422 idempotency_conflict. Nehmen Sie einen Hash der URL statt der URL selbst auf: Ein Schlüssel mit mehr als 200 Zeichen wird mit 400 abgelehnt. - Das Scan-Land. Die Vorschau wird aus dem Land gescannt, das beim Anlegen des Tests gewählt wurde. Liegt die Vorschau hinter einer IP-Allowlist, geben Sie die Adresse dieses Landes frei.
Alle Felder des Request-Bodys beschreibt die OpenAPI-Spezifikation.
Auf das Ergebnis warten
Den Zustand des Scans lesen Sie mit GET /api/v1/scans/{id}?wait=N. Der Server hält die Anfrage offen, bis der Scan beendet und seine Befunde gespeichert sind, höchstens N Sekunden. Er wartet nie länger als 90 Sekunden; wiederholen Sie die Anfrage, bis der Scan beendet ist.
Der Scan ist beendet, wenn sein status einer dieser Werte ist:
| Wert in der API | Im Dashboard |
|---|---|
completed |
Abgeschlossen |
failed |
Fehlgeschlagen |
cancelled |
Abgebrochen |
broken_step |
Abgebrochener Schritt |
no_data |
Keine Daten |
Wiederholen Sie, bis status endgültig und settled gleich true ist. settled: false heißt, dass Ihre Wartezeit abgelaufen ist, während die Befunde noch gespeichert wurden. Fragen Sie einfach erneut.
Ein Schlüssel darf höchstens fünf wartende Anfragen gleichzeitig halten; die sechste bekommt 429 too_many_long_polls. Wenn Sie mehrere Journeys auf einmal starten, lesen Sie sie nacheinander, nicht parallel.
Das Gate entwerfen
Das Gate übergeben Sie beim Lesen des Scans als ?gate= mit URL-kodiertem JSON. Prädikate werden mit ODER verknüpft: Das Gate schlägt fehl, sobald eines zutrifft. Alle Prädikate stehen in der Referenz. Für eine Vorschau und den dataLayer ist das ein vernünftiger Anfang:
{ "failOnSeverity": "critical", "failOnBrokenStep": true }
Regeln, die Sie der Prädikat-Tabelle nicht ansehen:
- Ein Scan mit dem Ergebnis
failed,cancelledoderno_databesteht nie ein Gate, egal was Sie einstellen. Ein Scan ohne Daten bekommt auch kein falsches „Score unter Schwelle“, sondern einen eigenen Code. - Ein abgebrochener Schritt lässt das Gate standardmäßig nicht scheitern. Er endet als Warnung, und die Abweichungen dieses Scans werden nicht ausgewertet. Zerstört eine Änderung den Warenkorb-Button, besteht ein Gate ohne
failOnBrokenStep. Deshalb empfehlen wir, es einzuschalten. - Ein Ereignis, das um einen Schritt verrutscht, liegt in der Toleranz: Es erscheint nicht unter den Abweichungen und lässt das Gate nie scheitern. Ein Ereignis, das um mehr als einen Schritt verrutscht, ist eine Abweichung
event_driftedmit der Schwerewarningund lässt deshalb ein Gate mitfailOnSeverity: "warning"oderfailOnNewDataLayerRegressionsscheitern. - Solange eine Journey noch keinen erwarteten Zustand hat, haben die dataLayer-Prädikate nichts zum Vergleichen. Sie können nicht scheitern, und in
warningsstehtdatalayer_gate_no_baseline. Es lohnt sich, in der CI auf diese Warnung zu reagieren. - Ohne
?gate=enthält die Antwort gar kein Gate. Ihr Skript muss ein fehlendesgate.passeddeshalb als Fehlschlag werten, nicht als Erfolg. minComplianceScorebewertet den Compliance-Score und gehört daher zu einem Compliance-Test. Eine Journey wird an ihren Abweichungen gemessen.- Eine Seite ohne Tracking-Tools und ohne Consent-Banner besteht das Gate nicht, der Code ist
no_tracking_observed. - Ein App-Lauf ohne geladenen GTM-Container besteht ein Diff-Gate nicht, der Code ist
vendors_not_evaluated. - Ein fehlgeschlagener Scan, der auf Seiten von AnalyticsProof unterbrochen wurde (zum Beispiel durch einen Neustart, ein Herunterfahren oder einen Ausfall unserer Datenbank), lässt das Gate mit dem Code
infrastructure_interruptedstattstatus_failedscheitern, ohne zusätzlichen Score-Code. Das Ergebnis sagt nichts über die Seite aus, starten Sie den Scan deshalb erneut.
Der Parameter scope=direct_datalayer beschränkt die Abweichungen auf das, was die Website direkt in window.dataLayer oder window.adobeDataLayer schreibt. Anfragen, die GA4, Adobe Analytics und andere Tools daraus erzeugen, bleiben im Dashboard, beeinflussen das Gate aber nicht. Die App nutzt dieselbe Einschränkung. Verglichen werden das Vorhandensein von Ereignissen und Parametern und ihr JavaScript-Typ, nicht ihre Werte. Die Ausnahme sind Consent-Signale: Dort ist ein geänderter Wert ein eigener Befund.
SARIF und der Kommentar
Mit ?format=sarif liefert ein beendeter Scan, dessen Befunde gespeichert sind, ein SARIF-2.1.0-Dokument. Bis dahin ist die Antwort das normale JSON: bei einem noch laufenden Scan und bei einem beendeten Scan, dessen Befunde noch gespeichert werden, mit settled: false. Ein unvollständiges SARIF erhalten Sie also nie, eventuell aber JSON an seiner Stelle. Am einfachsten holen Sie SARIF nach einer JSON-Antwort, deren status final ist und deren settled true ist. SARIF enthält dieselben Befunde, die das Gate bewertet (kritisch als error, Warnung als warning, Information als note); um einen Schritt verschobene Ereignisse stehen nicht darin. Das Urteil steht nicht darin; über den Job entscheidet weiterhin gate.passed. Geben Sie denselben scope mit, damit die Datei zum Gate passt.
Einen Kommentar für den Pull oder Merge Request liefert POST /api/v1/scans/{id}/explain: eine Erklärung der Abweichungen in Alltagssprache, ohne Parameterwerte. Vor dem Ende des Scans antwortet der Aufruf mit 409 scan_not_terminal.
Beispiel: GitLab CI und andere CIs
Die Schritte sind in jeder CI gleich, die curl und jq hat:
- Schlüssel und Test-ID als maskierte CI-Variablen speichern.
- Den Job nach dem Vorschau-Deployment einplanen, damit er die Vorschau-Adresse kennt.
- Einen Dry-Run mit
url,"journeys": "all"und einemIdempotency-Keyaus Commit, CI-Lauf, Test-ID, Journey-Auswahl und einem Hash der Vorschau-URL starten. AllepollUrlausscansbehalten und bei nicht leeremfailedabbrechen. - Jeden Scan mit
wait=90, dem Gate und demscopelesen, bis er endgültig und abgeschlossen ist. Das gesamte Warten über das Job-Timeout begrenzen. - Den Job scheitern lassen, wenn
gate.passedbei irgendeiner Journey nichttrueist.gate.failuresundwarningsins Log schreiben. - Zeigt Ihre CI kein SARIF an, die Datei als Job-Artefakt ablegen.
Ein vollständiges GitLab-Skript steht in den CI-Rezepten. Es spielt eine Journey gegen die Vorschau des Merge Requests ab, mit dem Body {"url": "…", "scanType": "web_journey"}, einem Idempotency-Key aus Commit, Job und einem Hash der Vorschau-URL und dem Journey-Gate von oben, und liest eine einzelne id. Für "journeys": "all" braucht es eine Schleife über scans[], weil die Antwort keine id auf oberster Ebene hat.
Fehler und was zu tun ist
| Code | Übliche Ursache | Was tun |
|---|---|---|
missing_credentials, invalid_key |
Das Secret kam nicht im Job an oder ist abgeschnitten | Variable und Header prüfen |
key_revoked, key_expired |
Der Schlüssel ist widerrufen oder abgelaufen | Neuen anlegen und das Secret tauschen |
plan_required |
Der Tarif enthält den Aufruf nicht (error.meta.minTier) |
Ein Schlüssel braucht Starter, ein Vorschau-Lauf Professional |
insufficient_permission |
Die Berechtigung des Schlüssels reicht nicht | Zum Starten ist Bearbeiten nötig, zum Lesen genügt Lesen |
invalid_request mit details[].code journey_required |
Der Test hat keine Journey zum Abspielen | Eine Journey aufzeichnen |
journey_sleeping |
Die Journey liegt über dem Limit des Tarifs | Reihenfolge der Journeys oder Tarif ändern |
step_cap_exceeded |
Die Journey hat mehr Schritte, als der Tarif erlaubt | Journey kürzen oder Tarif ändern |
scan_in_progress |
Ein normaler Scan des Tests läuft bereits | Warten oder eine url für einen Dry-Run senden |
idempotency_conflict |
Derselbe Schlüssel für eine andere Anfrage | Für einen anderen Body einen neuen Schlüssel verwenden |
rate_limited, too_many_long_polls |
Mehr als 60 Anfragen pro Minute oder fünf Wartende gleichzeitig | So lange warten, wie Retry-After angibt |
Codes in gate.failures sind keine API-Fehler, sondern die Gründe, warum das Gate nicht bestanden hat. Alle Codes sind in der Fehlerübersicht beschrieben.
Verwandte Anleitungen
Haben Sie in dieser Anleitung einen Fehler gefunden, oder fehlt etwas? Schreiben Sie uns