API-Referenz

Stand: 18. August 2026

Basis-URL: https://api.valyda.ch

Es gibt zwei Klassen von Endpunkten. Unter /v1 liegt alles, was deinem Server gehört; es verlangt den API-Schlüssel. Unter /public liegt, was der Browser deiner Kundin aufrufen darf; es verlangt das Einweg-Token einer einzelnen Verifikation.

Authentifizierung

Authorization: Bearer <api-key>

Fehlt der Kopfzeileneintrag oder ist der Schlüssel unbekannt oder zurückgezogen, antwortet die API mit 401 und {"error": "unauthorized"}.

Jeder Schlüssel gehört genau einem Konto. Eine Verifikation ist nur mit dem Schlüssel lesbar, mit dem sie erstellt wurde – die Zuordnung steckt in der Datenbankabfrage, nicht in einer Prüfung im Anwendungscode.

Verifikation erstellen

POST /v1/verifications
Feld Typ Pflicht Bedeutung
profile Text ja age-over-18, age-over-16 oder identity-full
client_reference Text nein Deine eigene Referenz, kommt unverändert zurück
{"profile": "age-over-18", "client_reference": "bestellung-4711"}

Antwort 201:

Feld Bedeutung
id Kennung der Verifikation, für die Statusabfrage
status Immer PENDING
profile Das angeforderte Profil
deeplink URL, die die Wallet öffnet – Ziel des QR-Codes
expires_at Ablaufzeitpunkt, ISO 8601
public_token Einweg-Token für den Browser

public_token ist das Einzige aus dieser Antwort, das den Browser erreichen darf – zusammen mit id und, wenn du den QR selbst zeichnest, deeplink.

Status abfragen

GET /v1/verifications/<id>
Feld Bedeutung
id Kennung
profile Verwendetes Profil
status PENDING, SUCCESS, FAILED oder EXPIRED
satisfied true, false oder null – das eigentliche Ergebnis
failure_reason Grund bei FAILED, sonst null
channel eid oder external – womit geprüft wurde
assurance high bei der E-ID, external beim eigenen Verfahren
client_reference Deine Referenz
created_at Erstellt
completed_at Abgeschlossen, sonst null
expires_at Ablauf

Freigeben nur bei status === "SUCCESS" && satisfied === true. Warum beides, steht unter Ablauf einer Verifikation.

Eine unbekannte ID – oder eine, die einem anderen Konto gehört – ergibt 404 mit {"error": "not_found"}. Beides sieht gleich aus, und das ist Absicht.

Eigenes Verfahren bestätigen

POST /v1/verifications/<id>/confirm

Der Ausweichkanal. Wenn dein eigenes Prüfverfahren zum Schluss kommt, dass die Kundin alt genug ist, meldest du das hier – und die Verifikation gilt als bestanden, so als hätte die E-ID geantwortet.

{ "channel": "external" }

channel ist optional und derzeit nur external. Es geht nichts über die Prüfung selbst mit: keine Ausweisnummer, kein Geburtsdatum, nicht einmal eine Notiz. Gespeichert wird, dass ein Verfahren ausserhalb der E-ID geantwortet hat – mehr braucht dein Nachweis nicht, und mehr wollen wir nicht halten.

Die Antwort ist die übliche Statusform, ergänzt um channel und assurance. Das Ergebnis lautet assurance: "external" und nicht high: dein Verfahren ist nicht die staatliche E-ID, und der Nachweis muss das weiterhin sagen können.

Warum das nur mit dem API-Key geht und nicht mit dem Einweg-Token des Widgets: Eine Kundschaft, die ihre eigene Prüfung bestätigen kann, hat keine Prüfung. Der Aufruf gehört deshalb in dein Backend.

Antwort Bedeutung
200 Übernommen
404 Unbekannte ID – oder eine, die einem anderen Konto gehört
409 already_settled Es gibt bereits ein Ergebnis
409 expired Die Sitzung ist abgelaufen; erstelle eine neue

already_settled gilt ausdrücklich auch dann, wenn die E-ID zuvor mit FAILED geantwortet hat. Ein abgelehntes Ergebnis lässt sich nicht nachträglich überschreiben. Wer einer Kundschaft nach einem Fehlschlag ein anderes Verfahren anbieten will, startet eine neue Verifikation – das ist eine Entscheidung vorher, keine Korrektur hinterher.

Das Widget merkt die Bestätigung von selbst: Es fragt den öffentlichen Endpunkt weiter ab, auch wenn der Dialog geschlossen ist, und wechselt auf „bestätigt", sobald die Antwort da ist.

Öffentliche Endpunkte

Diese beiden sind für den Browser gedacht. Sie authentifizieren über das Einweg-Token der jeweiligen Verifikation, erlauben jede Herkunft (access-control-allow-origin: *) und werden nicht zwischengespeichert.

GET /public/verifications/<id>?token=<public_token>

Antwortet mit id, profile, status, satisfied, channel und expires_at – eine verkürzte Fassung ohne deine Referenz und ohne Zeitstempel, die den Browser nichts angehen.

GET /public/verifications/<id>/qr.svg?token=<public_token>

Liefert den QR-Code zum Deeplink als SVG. Damit musst du keine QR-Bibliothek einbinden.

Das Token steht bewusst in der Abfragezeichenkette und nicht in einer Kopfzeile: so bleibt jede Anfrage eine einfache CORS-Anfrage, und der Browser spart sich die Vorabanfrage. Fehlt das Token oder passt es nicht zur ID, antworten beide Endpunkte mit 404.

Weitere Endpunkte

GET /health

Antwortet mit {"status": "ok"}. Für deine Überwachung.

GET /widget/v1/valyda.js

Das Widget-Skript. Siehe Widget.

Fehler

Code Körper Ursache
400 {"error": "invalid_request", "details": […]} Feld fehlt oder hat den falschen Typ; details benennt die Stelle
401 {"error": "unauthorized"} Schlüssel fehlt, ist unbekannt oder zurückgezogen
404 {"error": "not_found"} Unbekannte ID, fremdes Konto oder falsches Token

Statt Abfragen: Webhooks

Wer im Sekundentakt abfragt, belastet beide Seiten. Für die serverseitige Benachrichtigung siehe Webhooks. Ein Rückfall auf Abfragen bleibt trotzdem sinnvoll – warum, steht dort.