API v3

Fragen zur API

Was Entwickler vor und während der Anbindung fragen: Schlüssel, Links, wie die Entscheidung auf Ihre Website kommt, Webhooks, Protokoll, Limits und Fehler. Jede Methode und jede Zahl hier ist die, mit der die API heute arbeitet.

Adresse
https://api.consent.link/v3
Schlüssel
Authorization: Bearer clk_live_…
Format
JSON, UTF-8, Antworten auf Englisch

Erste Schritte

Wie bekomme ich einen API-Schlüssel?

Registrieren Sie sich und bestätigen Sie Ihre E-Mail-Adresse. Die erste Seite nach der Anmeldung, «Über die API verbinden», erstellt Ihren Schlüssel clk_live_….

Der Schlüssel wird einmal angezeigt, direkt nach dem Erstellen. Kopieren Sie ihn dann: Wir speichern nur seinen Hash. Ein Konto kann bis zu 10 aktive Schlüssel haben. Einen Schlüssel, den Sie nicht mehr brauchen, widerrufen Sie auf derselben Seite; er funktioniert sofort nicht mehr.

Wo ist die API und wie sende ich den Schlüssel?

Die API liegt unter https://api.consent.link/v3 und spricht JSON. Senden Sie den Schlüssel bei jeder Anfrage im Header Authorization: Bearer clk_live_… (der Header X-AUTH-TOKEN unserer alten API v2 funktioniert auch). Eine Anfrage ohne gültigen Schlüssel bekommt 401 unauthenticated.

Rufen Sie die API nur von Ihrem Server aus auf. Wer den Schlüssel hat, kann Links erstellen und Ihr Protokoll mit vollständigen Besucherdaten lesen.

Anfrage
curl https://api.consent.link/v3/usage \
  -H "Authorization: Bearer clk_live_…"
Antwort
{
  "consents": 412,
  "limit": 1000,
  "remaining": 588,
  "period_start": "2026-11-01T00:00:00.000Z",
  "period_end": "2026-12-01T00:00:00.000Z"
}

Welche Methoden gibt es?

Alle auf einen Blick. Jeder Pfad folgt auf https://api.consent.link; die öffentlichen Schlüssel zum Prüfen von Entscheidungstoken liegen unter https://app.consent.link/.well-known/jwks.json.

MethodeWas sie tut
POST/v3/linksLink erstellen
POST/v3/links/bulkBis zu 500 Links erstellen
GET/v3/linksLinks auflisten, mit Filtern
GET/v3/links/{id}Ein Link und seine Zahlen
PATCH/v3/links/{id}Link ändern
DELETE/v3/links/{id}Link abschalten
GET/v3/profilesSeitenprofile auflisten
POST/v3/profilesSeitenprofil erstellen
GET/v3/profiles/{id}Ein Seitenprofil
PATCH/v3/profiles/{id}Profil ändern oder veröffentlichen
DELETE/v3/profiles/{id}Profil löschen
POST/v3/consents/verifyEntscheidungstoken einmal prüfen
GET/v3/webhooksWebhooks auflisten
POST/v3/webhooksWebhook hinzufügen
GET/v3/webhooks/{id}Ein Webhook
PATCH/v3/webhooks/{id}URL oder Ereignisse ändern, ein- und ausschalten
DELETE/v3/webhooks/{id}Webhook löschen
GET/v3/consentsDas Protokoll, mit Filtern
GET/v3/consents/{id}Ein Datensatz mit seinen Nachweisen
POST/v3/consents/{id}/withdrawEine Einwilligung im Namen des Besuchers widerrufen
GET/v3/consents/{id}/proofPDF-Nachweis eines DatensatzesPro und Business
GET/v3/consents/{id}/snapshotDie Seite, die der Besucher sah, als HTML
GET/v3/consents/{id}/snapshot.pngDie Seite, die der Besucher sah, als Screenshot
POST/v3/exportsCSV-Export startenPro und Business
GET/v3/exports/{id}Ein Export und sein StandPro und Business
GET/v3/exports/{id}/fileCSV herunterladenPro und Business
GET/v3/stats/summarySummen für einen Zeitraum
GET/v3/stats/dailyZahlen pro Tag (und Land)
GET/v3/usageEinwilligungen im Zeitraum und das Limit

Die Einwilligungsseite

Was sehen Besucher, und in welcher Sprache?

Die Seite, die Sie im Kundenbereich unter «Gestaltung» eingerichtet haben: die Textvorlage aus unserer Bibliothek, die Datenempfänger, die Kennzeichnung, den Link zu Ihrer Datenschutzerklärung und bei Business Ihr Logo und Ihre Farbe. type und template des Links haben Vorrang vor der Gestaltung. Wenn Sie eine neue Version veröffentlichen, ändern sich die Seiten Ihrer Links innerhalb einer Minute.

Die Sprache wird nach dem Browser des Besuchers gewählt: Englisch, Spanisch, Deutsch, Französisch, Italienisch, Portugiesisch oder Russisch. Sie wählen eine Vorlage, keine Worte: Die Texte stammen aus unserer Bibliothek, eigene Texte gibt es nicht.

Solange in «Gestaltung» nichts veröffentlicht ist, zeigt die Seite keinen Link zu Ihrer Datenschutzerklärung und keine Datenempfänger. Links werden trotzdem erstellt, und die Antworten auf POST /v3/links, POST /v3/links/bulk und PATCH /v3/links/{id} tragen den Header Consent-Link-Warning: design_unpublished, solange die Konfiguration des Links keine veröffentlichte Version hat.

Wie bekommen verschiedene Links eine verschiedene Gestaltung?

Speichern Sie unter «Gestaltung» oder über die API mehrere Profile der Seite, jedes mit eigener ID wie black-friday, und nennen Sie beim Erstellen eines Links eines davon: "profile": "black-friday" in POST /v3/links oder in einem Eintrag der Massenerstellung. Ohne profile bekommt der Link Ihr Standardprofil.

Ein Link behält sein Profil: Wird später ein anderes Profil zum Standard, bleiben alte Links unverändert. PATCH /v3/links/{id} mit profile verschiebt einen Link, "profile": null verschiebt ihn auf den Standard dieses Moments. Eine unbekannte ID ergibt 422 beim Feld profile. Das Link-Objekt zeigt sein profile, und GET /v3/links?profile=black-friday listet die Links eines Profils. Wie viele Profile Sie haben können, hängt vom Tarif ab: 1 bei Free, 5 bei Pro, 50 bei Business, das Standardprofil mitgezählt.

Wie verwalte ich Profile über die API?

GET /v3/profiles listet sie auf, das Standardprofil zuerst. POST /v3/profiles erstellt eines mit id und policy_url (Pflicht) und nach Wunsch name, template, vendors, type, remember, brand_name und default. Es wird sofort veröffentlicht. PATCH /v3/profiles/{id} ändert es: Die Felder der Seite veröffentlichen sofort eine neue Version, name und "default": true allein veröffentlichen nichts.

DELETE /v3/profiles/{id} löscht ein Profil, das nicht der Standard ist (409 profile_is_default) und keine aktiven Links hat (409 profile_in_use). Ein Profil über dem Tariflimit zu erstellen oder eines zu verwenden, das nach einem Downgrade über dem Limit liegt, ergibt 402 profiles_limit_reached. Änderungen brauchen einen Live-Schlüssel.

Was passiert mit den Parametern meiner Adresse, etwa utm oder gclid?

Mit Einwilligung kommt der Besucher genau auf die Adresse, die Sie angegeben haben. Ohne Einwilligung bleiben utm_* und Ihre anderen Parameter ebenfalls erhalten, entfernt werden nur die Klick-IDs von Anzeigen: gclid, gbraid, wbraid, fbclid, msclkid, ttclid, twclid, li_fat_id. In beiden Fällen hängen wir einen Parameter an, cl_ct, das Entscheidungstoken.

Es zählen nur die Parameter des Ziels selbst: Was an den Kurzlink angehängt wird (https://consent.link/nk7Qa?utm_source=…), wird nicht weitergegeben. Legen Sie alles, was Ihre Website braucht, in destination oder erstellen Sie einen Link pro Kampagne.

Wohin der Besucher geht: mit Einwilligung, dann ohne
https://shop.example/sale?utm_source=insta&gclid=Cj0K…&cl_ct=eyJhbGciOiJFUzI1NiIs…
https://shop.example/sale?utm_source=insta&cl_ct=eyJhbGciOiJFUzI1NiIs…

Entscheidungen und Webhooks

Wie erfährt meine Website, wie der Besucher entschieden hat?

Jede Weiterleitung von der Einwilligungsseite trägt cl_ct, ein signiertes Token (JWT, ES256), das 10 Minuten gilt. Prüfen Sie es von Ihrem Server aus mit POST /v3/consents/verify: Die Antwort enthält die Entscheidung, und jedes Token lässt sich nur einmal prüfen, eine kopierte Adresse ist also wertlos. Lesen Sie das Token und entfernen Sie es dann aus der Adresse.

Was es enthält: decision (granted oder denied), purposes (was der Besucher erlaubt hat), cid (der Datensatz im Protokoll), sid (das Pseudonym des Besuchers in Ihrem Konto, auf all Ihren Links gleich), cexp (bis wann die Einwilligung gilt, Unix-Zeit), link, mode, iat und exp. Keine IP, kein Browser, keine anderen personenbezogenen Daten. Ein Besucher, der früher eingewilligt und «Nicht erneut fragen» gelassen hat, geht direkt durch, mit granted.

Die Signatur können Sie auch selbst prüfen, mit den Schlüsseln unter https://app.consent.link/.well-known/jwks.json und einer beliebigen JWT-Bibliothek: Prüfen Sie exp und dass link einer Ihrer Links ist. Ihre eigene Prüfung erkennt nicht, ob ein Token schon verwendet wurde; die API-Methode schon. Fehler: 422 token_invalid, 422 token_expired, 409 token_used.

Anfrage
curl -X POST https://api.consent.link/v3/consents/verify \
  -H "Authorization: Bearer clk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"token": "eyJhbGciOiJFUzI1NiIs…"}'
Antwort
{
  "decision": "granted",
  "purposes": ["stats", "ads"],
  "cid": "01jbz8m3k4x9w2r7d5t6q8v1yh",
  "sid": "psd_Qm9xV3c…",
  "cexp": 1809345604,
  "link": "nk7Qa",
  "mode": "live",
  "iat": 1793712004,
  "exp": 1793712604
}

Wie bekomme ich Entscheidungen sofort auf meinen Server?

Erstellen Sie einen Webhook: POST /v3/webhooks mit url und den gewünschten events (Tabelle unten). Die Adresse muss https mit öffentlichem Hostnamen sein. Die Antwort enthält secret (whsec_…) zum Prüfen der Signaturen: Es wird einmal angezeigt, speichern Sie es. Ein Konto kann bis zu 10 aktive Webhooks haben.

Das Zustellungsprotokoll, die Schaltfläche «Wiederholen» und das Ab- und Einschalten eines Webhooks finden Sie im Kundenbereich unter «Konto» → «Webhooks».

Anfrage
curl -X POST https://api.consent.link/v3/webhooks \
  -H "Authorization: Bearer clk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://shop.example/hooks/consent",
    "events": ["consent.recorded", "consent.withdrawn"]
  }'
Antwort
201 Created
{
  "id": "wh_01jbz9a7c2m5n8p3q6r9s2t5v8",
  "url": "https://shop.example/hooks/consent",
  "events": ["consent.recorded", "consent.withdrawn"],
  "mode": "live",
  "status": "active",
  "secret": "whsec_…",
  "created_at": "2026-11-03T12:00:00.000Z"
}

Welche Ereignisse gibt es, und was enthalten sie?

Jedes Ereignis ist ein POST mit JSON-Body: id (evt_…), type, created_at, mode und data. Ein Einwilligungsdatensatz in data.consent hat id, link, decision, purposes, scope (domain, wenn der Besucher «Nicht erneut fragen» gelassen hat, sonst link), sid, dest_domain, country, notice_version, created_at und expires_at. Nie eine IP, einen Browser oder ein Cookie.

EreignisWanndata
consent.recordedEin neuer Datensatz im Protokoll: eine Einwilligung, eine Ablehnung oder ein Besucher, der früher eingewilligt hatconsent
consent.withdrawnEine Einwilligung wurde widerrufen: vom Besucher unter «Meine Einwilligungen» oder von Ihnen über die APIconsent, withdraws
link.blockedWir haben einen Link gesperrt, weil sein Ziel sich als gefährlich erwieslink, destination, reason, message
export.readyEin CSV-Export ist fertig (oder fehlgeschlagen)id, status, filter, rows, bytes, sha256, file, expires_at
usage.warning80 % oder 100 % der Einwilligungen des Zeitraums sind verbrauchtpercent, consents, limit, period_start, period_end
consent.expiredEine Einwilligung ist abgelaufen: 6 Monate nach ihrer Erteilung, ohne vorherigen Widerrufconsent, expired_at
Was Ihr Server bekommt
POST /hooks/consent
Content-Type: application/json
Consent-Event: consent.recorded
Consent-Delivery: whd_01jbz9d4f7h0k3m6p9r2t5w8y1
Consent-Signature: t=1793712005,v1=5f3a…

{
  "id": "evt_01jbz9d4f6g9j2l5n8q1s4v7x0",
  "type": "consent.recorded",
  "created_at": "2026-11-03T12:00:05.012Z",
  "mode": "live",
  "data": {
    "consent": {
      "id": "01jbz8m3k4x9w2r7d5t6q8v1yh",
      "link": "nk7Qa",
      "decision": "granted",
      "purposes": ["stats", "ads"],
      "scope": "domain",
      "sid": "psd_Qm9xV3c…",
      "dest_domain": "shop.example",
      "country": "DE",
      "notice_version": "statsads-v1",
      "created_at": "2026-11-03T12:00:04.398Z",
      "expires_at": "2027-05-03T12:00:04.398Z"
    }
  }
}

Wie prüfe ich, dass ein Webhook von Ihnen kommt?

Jede Anfrage trägt die Header Consent-Event, Consent-Delivery und Consent-Signature: t=1793712000,v1=5f3a…. v1 ist der HMAC-SHA256 in Hex aus t, einem Punkt und dem unveränderten Body der Anfrage, mit dem Secret Ihres Webhooks als Schlüssel.

Berechnen Sie ihn aus dem Body genau so, wie er angekommen ist (vor jedem JSON-Parsing), vergleichen Sie in konstanter Zeit und lehnen Sie ein t ab, das mehr als fünf Minuten von Ihrer Uhr abweicht, damit eine mitgeschnittene Anfrage nicht wiederholt werden kann.

PHP
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_CONSENT_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', ($sig['t'] ?? '').'.'.$body, $secret);
if (abs(time() - (int) ($sig['t'] ?? 0)) > 300 || ! hash_equals($expected, $sig['v1'] ?? '')) {
    http_response_code(400);
    exit;
}
Node.js
const crypto = require('node:crypto');
const sig = Object.fromEntries(req.get('Consent-Signature').split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${sig.t}.${rawBody}`).digest('hex');
const ok = Math.abs(Date.now() / 1000 - Number(sig.t)) <= 300
  && typeof sig.v1 === 'string' && sig.v1.length === expected.length
  && crypto.timingSafeEqual(Buffer.from(sig.v1), Buffer.from(expected));

Was, wenn mein Server beim Senden eines Ereignisses nicht erreichbar ist?

Eine Zustellung zählt, wenn Ihr Server innerhalb von 10 Sekunden mit 2xx antwortet; Weiterleitungen werden nicht verfolgt. Sonst versuchen wir es erneut und warten zwischen den Versuchen 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 12 Stunden, insgesamt 6 Versuche. Nach dem letzten gilt die Zustellung als fehlgeschlagen und bleibt im Protokoll, wo «Wiederholen» sie bis zu 3 Tage nach dem Ereignis erneut sendet.

Antworten Sie also schnell und erledigen Sie die Arbeit danach. Dasselbe Ereignis kann mehr als einmal ankommen, etwa nach einer Zeitüberschreitung: Überspringen Sie bereits verarbeitete id. Ereignisse kommen nicht immer in der Reihenfolge an, in der sie geschahen.

Wie wird eine Einwilligung widerrufen, und was passiert dann?

Besucher sehen und widerrufen ihre Einwilligungen unter «Meine Einwilligungen». Wendet sich ein Besucher stattdessen an Sie, widerrufen Sie für ihn: POST /v3/consents/{id}/withdraw ohne Body, mit der ID seines Datensatzes granted oder reused. Das geht für jede noch gültige Einwilligung, egal wie weit Ihr Tarif das Protokoll zurück zeigt. Die Antwort ist 201 mit dem neuen Datensatz withdrawn, dessen first_consent_id die widerrufene Einwilligung ist. Ein Datensatz, der keine gültige Einwilligung ist (abgelehnt, schon widerrufen oder abgelaufen), bekommt 409 consent_not_in_force; einer, den Sie nicht lesen dürfen, 404 wie bei GET /v3/consents/{id}.

Beide Wege enden gleich: ein Datensatz withdrawn im Protokoll, consent.withdrawn an Ihren Webhook mit data.withdraws, der ID der widerrufenen Einwilligung, und ab dann fragen Ihre Links diesen Besucher wieder. Ein Widerruf schließt alle Einwilligungen, die der Besucher vorher für diese Domain gegeben hat, und zählt nicht zu Ihrem Limit.

Verwenden Sie seine Daten auf Ihrer Seite nicht mehr: Ordnen Sie über sid oder die Datensatz-ID zu.

Anfrage
curl -X POST https://api.consent.link/v3/consents/01jbz8m3k4x9w2r7d5t6q8v1yh/withdraw \
  -H "Authorization: Bearer clk_live_…"
Antwort
201 Created
{
  "id": "01jc4t7v0x3z6b9d2f5h8k1m4p",
  "link": "nk7Qa",
  "decision": "withdrawn",
  "purposes": [],
  "scope": null,
  "sid": "psd_Qm9xV3c…",
  "dest_domain": "shop.example",
  "country": null,
  "notice_version": null,
  "created_at": "2026-12-14T09:30:12.051Z",
  "expires_at": null,
  "first_consent_id": "01jbz8m3k4x9w2r7d5t6q8v1yh",
  "gpc": false,
  "settings_version": null,
  "evidence": null,
  "chain": {…}
}

Wie lange gilt eine Einwilligung?

Eine Einwilligung gilt 6 Monate ab ihrer Erteilung: expires_at im Datensatz, cexp im Entscheidungstoken. Ein Datensatz reused trägt das expires_at der Einwilligung, die er wiederverwendet. Danach fragen Ihre Links den Besucher wieder.

Läuft eine Einwilligung granted ab, ohne widerrufen worden zu sein, bekommt Ihr Webhook consent.expired mit data.consent, dem Datensatz, und data.expired_at. Wir prüfen einmal pro Stunde, das Ereignis kommt also innerhalb der Stunde nach expires_at, einmal pro Einwilligung. Datensätze reused bekommen kein eigenes Ereignis: Das der wiederverwendeten Einwilligung deckt sie ab. Das Ereignis geht nur an Webhooks mit consent.expired in events. Ein Ablauf wird einmal gemeldet, auch wenn damals kein Webhook darauf wartete: Später kommt er nicht noch einmal.

Was Ihr Server bekommt
POST /hooks/consent
Content-Type: application/json
Consent-Event: consent.expired
Consent-Delivery: whd_01jxq3m6p9r2t5w8y1b4d7f0h3
Consent-Signature: t=1809349502,v1=9c1e…

{
  "id": "evt_01jxq3m6p8q1s4v7x0z3b6d9f2",
  "type": "consent.expired",
  "created_at": "2027-05-03T13:05:02.117Z",
  "mode": "live",
  "data": {
    "consent": {
      "id": "01jbz8m3k4x9w2r7d5t6q8v1yh",
      "link": "nk7Qa",
      "decision": "granted",
      "purposes": ["stats", "ads"],
      "scope": "domain",
      "sid": "psd_Qm9xV3c…",
      "dest_domain": "shop.example",
      "country": "DE",
      "notice_version": "statsads-v1",
      "created_at": "2026-11-03T12:00:04.398Z",
      "expires_at": "2027-05-03T12:00:04.398Z"
    },
    "expired_at": "2027-05-03T12:00:04.398Z"
  }
}

Website und Banner

Wie wendet meine Website die Entscheidung selbst an (Google Consent Mode v2)?

Binden Sie unser Skript im <head> vor GTM und gtag ein: https://consent.link/js/consent.js. Es setzt den Standard von Consent Mode v2 (alles abgelehnt außer functionality_storage und security_storage, mit wait_for_update 500 ms), liest cl_ct aus der Adresse, prüft die Signatur mit den Schlüsseln unter https://app.consent.link/.well-known/jwks.json direkt im Browser und aktualisiert die Einwilligung.

Die Entscheidung wird im Cookie cl_consent auf Ihrer Domain gespeichert: bei Einwilligung bis cexp, bei Ablehnung 180 Tage, sodass die nächsten Seiten kein Banner brauchen. Nach der Prüfung verschwindet cl_ct aus der Adressleiste, Ihre anderen Parameter bleiben. Das Skript sendet uns nichts.

Wenn Sie die Entscheidung bei sich speichern, prüfen Sie das Token zusätzlich von Ihrem Server mit POST /v3/consents/verify: Das Skript weiß nicht, ob ein Token schon benutzt wurde.

HTML
<script src="https://consent.link/js/consent.js"></script>

Wie richte ich es im Google Tag Manager ein?

Laden Sie die Vorlage consent-link.tpl herunter, öffnen Sie in GTM Vorlagen → Neu → ⋮ → Importieren und speichern Sie. Legen Sie damit ein Tag mit dem Trigger «Consent Initialization – All Pages» («Einwilligungsinitialisierung – Alle Seiten») an.

Das Tag setzt die Standardwerte, wendet die im Cookie cl_consent gespeicherte Entscheidung an und lädt das Skript, das ein frisches cl_ct prüft. Das Skript im <head> brauchen Sie dann nicht zusätzlich.

Welche Consent-Mode-Signale gibt jeder Zweck frei?

Die purposes des Tokens geben diese Signale frei. Was nicht freigegeben ist, bleibt abgelehnt; eine Ablehnung (denied) lehnt alles ab außer den zwei, die immer erlaubt sind.

ZweckConsent-Mode-Signale
stats analytics_storage
ads ad_storage, ad_user_data, ad_personalization
app_install ad_storage, ad_user_data
personalization personalization_storage
Immer functionality_storage, security_storage

Ich nutze Cookiebot, OneTrust oder ein anderes Banner. Was mache ich damit?

Zeigen Sie kein zweites Banner, wenn jemand schon auf unserer Seite entschieden hat. Das Skript liefert die Entscheidung in window.consentLink.decision (granted, denied oder null) und window.consentLink.has('stats'); window.consentLink.ready erfüllt sich, sobald ein Token in der Adresse geprüft ist. Dazu kommen das Ereignis consentlink:update auf document und consent_link_update im dataLayer. Laden Sie unser Skript vor dem des Banners.

Cookiebot: Geben Sie die Entscheidung mit Cookiebot.submitCustomConsent weiter, dann erscheint sein Banner nicht. OneTrust hat keine öffentliche Funktion, um eine Einwilligung von der Website aus zu speichern; laden Sie sein Skript deshalb nur, wenn keine Entscheidung vorliegt. Andere Banner ebenso: Zwecke über deren API weitergeben oder das Banner nicht laden.

Cookiebot
window.addEventListener('CookiebotOnLoad', function () {
  window.consentLink.ready.then(function (cl) {
    if (cl.decision && !Cookiebot.hasResponse) {
      Cookiebot.submitCustomConsent(cl.has('personalization'), cl.has('stats'), cl.has('ads'));
    }
  });
});
OneTrust
window.consentLink.ready.then(function (cl) {
  if (cl.decision) return; // decided on consent.link: no second banner
  var s = document.createElement('script');
  s.src = 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.js';
  s.setAttribute('data-domain-script', 'YOUR-ONETRUST-ID');
  document.head.appendChild(s);
});

Protokoll, Nachweis und Statistik

Wie lese ich das Protokoll?

GET /v3/consents listet die Datensätze auf, die neuesten zuerst, mit den Filtern decision (granted, denied, reused, withdrawn), link, country (DE), from, to und denselben Seiten wie bei Links. GET /v3/consents/{id} gibt einen Datensatz mit seinen Nachweisen zurück (IP-Adresse, Browser, Sprache, Hash des Seitentexts, wann die Seite gezeigt wurde und wie lange der Besucher brauchte) und seinen Platz in der Hash-Kette, chain.

Eine Ablehnung speichert nur die Entscheidung, die Zeit, den Link und das Land, ihr evidence ist daher null. Die API liefert die Daten vollständig; der Kundenbereich verbirgt einen Teil davon, bis Sie die Anmeldung mit einem Code aus einer App einschalten. Wie weit zurück Sie sehen, hängt vom Tarif ab (Tabelle unter «Tarife und Limits»).

Anfrage
curl "https://api.consent.link/v3/consents?decision=granted&from=2026-11-01&limit=100" \
  -H "Authorization: Bearer clk_live_…"

Wie bekomme ich einen PDF-Nachweis einer Einwilligung?

GET /v3/consents/{id}/proof liefert den Datensatz als PDF: die Entscheidung, wer, was gezeigt wurde, die Hashes und wie man sie prüft. Es ist in der Sprache Ihres Kundenbereichs. Verfügbar bei Pro und Business (bei Free 402 plan_required), bis zu 20 pro Minute. Lässt sich das PDF gerade nicht erstellen, lautet die Antwort 503 unavailable mit Retry-After.

Anfrage
curl -o proof.pdf https://api.consent.link/v3/consents/01jbz8m3k4x9w2r7d5t6q8v1yh/proof \
  -H "Authorization: Bearer clk_live_…"

Wie sehe und prüfe ich die Seite, auf der der Besucher eingewilligt hat?

Jeder Datensatz granted trägt evidence.snapshot_hash: den SHA-256 der Einwilligungsseite, wie der Besucher sie gesehen hat, als HTML. Ein Snapshot gilt für alle Besucher derselben Seite, also derselben Gestaltungs- und Textversion, Sprache und Linkart. GET /v3/consents/{id}/snapshot liefert dieses HTML mit dem Hash im Header X-Snapshot-Hash, GET /v3/consents/{id}/snapshot.png einen Screenshot davon. Für beide gelten die Regeln von GET /v3/consents/{id}, einschließlich der Protokolltiefe.

Zum Prüfen bilden Sie den SHA-256 des HTML genau so, wie es ankam: Er ist gleich snapshot_hash, und der eigene Hash des Datensatzes in der Kette deckt snapshot_hash ab, sodass die Seite nachträglich nicht ausgetauscht werden kann. Was sich von Aufruf zu Aufruf ändert, ist im Snapshot festgelegt: Der Host des Ziels lautet example.com, die Signatur des Formulars ist leer und seine action #. Die echte Adresse steht in dest_domain und evidence.destination.

Datensätze reused, Ablehnungen und Datensätze aus der Zeit vor den Snapshots haben snapshot_hash null, und ihr Snapshot antwortet mit 404 not_found («This record has no page snapshot.»). Direkt nach einer Entscheidung werden die Dateien eventuell noch geschrieben: 404 not_found mit «The page snapshot is not ready yet.» heißt, es gleich noch einmal zu versuchen. Das HTML ist nach Sekunden da, der Screenshot etwas später. Die Seite des Datensatzes im Kundenbereich und der PDF-Nachweis zeigen Screenshot und Hash ebenfalls.

Anfrage
curl -o page.html https://api.consent.link/v3/consents/01jbz8m3k4x9w2r7d5t6q8v1yh/snapshot \
  -H "Authorization: Bearer clk_live_…"

sha256sum page.html

Wie exportiere ich das Protokoll als CSV?

POST /v3/exports mit den Filtern von GET /v3/consents (decision, link, country, from, to) antwortet sofort mit 202 und der id des Exports. Die Datei entsteht im Hintergrund: Sie bekommen den Webhook export.ready, oder Sie fragen GET /v3/exports/{id} ab, bis status gleich ready ist, und laden dann GET /v3/exports/{id}/file herunter. Der Export enthält außerdem rows und den sha256 der Datei.

Dateien werden 7 Tage aufbewahrt (danach 410 export_expired). Bis zu 3 Exporte können gleichzeitig in Arbeit sein (429 too_many_exports). Pro und Business.

Anfrage
curl -X POST https://api.consent.link/v3/exports \
  -H "Authorization: Bearer clk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "granted",
    "from": "2026-10-01",
    "to": "2026-11-01"
  }'
Antwort
202 Accepted
{
  "id": "exp_01jbz9f2h5k8m1p4r7t0w3y6a9",
  "type": "csv",
  "status": "queued",
  "source": "api",
  "filter": {
    "decision": "granted",
    "from": "2026-10-01T00:00:00.000Z",
    "to": "2026-11-01T00:00:00.000Z"
  },
  "rows": null,
  "bytes": null,
  "sha256": null,
  "created_at": "2026-11-03T12:00:00.000Z",
  "finished_at": null,
  "expires_at": null,
  "file": null
}

Bekomme ich Statistik und meinen Verbrauch über die API?

GET /v3/stats/summary liefert shown, granted, denied, left, reused und withdrawn für UTC-Tage von from bis to (2026-10-31, beide eingeschlossen; standardmäßig die letzten 30 Tage, höchstens 366 Tage, so weit zurück, wie Ihr Tarif das Protokoll zeigt). Mit link für einen Link. GET /v3/stats/daily liefert dieselben Zahlen pro Tag und bei Pro und Business mit by=country nach Ländern. Bots werden nie gezählt.

GET /v3/usage zeigt die Einwilligungen dieses Zeitraums, Ihr limit, was davon remaining ist, sowie Beginn und Ende des Zeitraums.

Anfrage
curl "https://api.consent.link/v3/stats/summary?from=2026-10-01&to=2026-10-31&link=nk7Qa" \
  -H "Authorization: Bearer clk_live_…"
Antwort
{
  "from": "2026-10-01",
  "to": "2026-10-31",
  "link": "nk7Qa",
  "shown": 1840,
  "granted": 1215,
  "denied": 402,
  "left": 198,
  "reused": 25,
  "withdrawn": 3
}

Tarife und Limits

Was enthält jeder Tarif?

Die API, Webhooks und die Einwilligungsseite in sieben Sprachen gibt es in jedem Tarif. Die Tarife unterscheiden sich in Einwilligungen pro Monat, wie weit zurück das Protokoll gezeigt wird, der Anfragerate und zusätzlichen Funktionen. Preise in Euro, ohne MwSt.

TarifPreis pro MonatEinwilligungen pro MonatProtokollAnfragen pro MinuteSeitenprofileAußerdem
Free0 €1.000Protokoll für 30 Tage601—
Pro9 €10.000Protokoll für 1 Jahr3005CSV-Export und PDF-Nachweis · Statistik nach Tag und Land
Business99 €250.000Protokoll für 3 Jahre1.00050CSV-Export und PDF-Nachweis · Statistik nach Tag und Land · Ihr Logo und Ihre Farbe · Links auf Ihrer eigenen Domain

Was passiert, wenn meine Einwilligungen aufgebraucht sind?

Es zählen nur neue Einwilligungen: Ablehnungen, wiederholte Besuche mit «Nicht erneut fragen», und Bots nicht. Bei 80 % und 100 % des Limits bekommen Sie eine E-Mail und den Webhook usage.warning.

Ist das Limit aufgebraucht, werden neue Live-Links mit 402 limit_reached abgelehnt. Alles andere läuft weiter: Bestehende Links fragen und protokollieren wie bisher, Webhooks und Token kommen wie gewohnt, nichts wird verborgen. Links lassen sich wieder erstellen, sobald der nächste Zeitraum beginnt (bei Free am 1. des Monats, bei bezahlten Tarifen am Tag der Zahlung) oder direkt nach einem Upgrade.

Wie viele Anfragen darf ich senden?

Pro Konto, alle Schlüssel zusammen: 60 pro Minute bei Free, 300 bei Pro, 1.000 bei Business. Darüber lautet die Antwort 429 rate_limited mit einem Header Retry-After; die Header X-RateLimit-Limit und X-RateLimit-Remaining zeigen, wo Sie stehen. Manche Methoden haben zusätzlich ein eigenes Limit: PDF-Nachweise 20 pro Minute, neue Exporte 10, Export-Downloads 30.

Fehler und Sicherheit

Wie sehen Fehler aus?

Immer gleich: der HTTP-Status und ein JSON-Body mit einem festen code und einer englischen message. Richten Sie sich nach dem code, die Meldung kann umformuliert werden. Ungültige Felder ergeben 422 validation_failed mit fields: jedes Feld mit seinen Problemen.

StatusCodeBedeutung
401unauthenticatedKein Schlüssel, oder der Schlüssel ist falsch oder widerrufen
402limit_reachedDie Einwilligungen des Zeitraums sind aufgebraucht: keine neuen Live-Links
402plan_requiredDie Funktion braucht einen höheren Tarif
402profiles_limit_reachedMehr Seitenprofile, als der Tarif erlaubt
403account_blockedDas Konto ist gesperrt: Wenden Sie sich an den Support
403links_blockedDas Konto darf keine Links erstellen: Wenden Sie sich an den Support
403mode_mismatchEinen Live-Link ändert ein Live-Schlüssel, einen Testlink ein Test-Schlüssel
403live_key_requiredDafür braucht es einen Live-Schlüssel
404not_foundIn Ihrem Konto gibt es nichts mit dieser ID
405method_not_allowedDiese HTTP-Methode ist hier nicht erlaubt
409link_disabledDer Link ist abgeschaltet und lässt sich nicht ändern
409link_blockedDer Link wurde gesperrt und lässt sich nicht ändern
409idempotency_in_progressEine Anfrage mit diesem Idempotency-Key läuft noch
409token_usedDieses Entscheidungstoken wurde schon geprüft
409export_not_readyDer Export wird noch erstellt
409export_failedDer Export ist fehlgeschlagen: Starten Sie einen neuen
409consent_not_in_forceDer Datensatz ist keine gültige Einwilligung: abgelehnt, widerrufen oder abgelaufen
409profile_is_defaultDas Standardprofil lässt sich nicht löschen
409profile_in_useDas Profil hat noch aktive Links
410export_expiredDie Datei wird nicht mehr aufbewahrt: Starten Sie einen neuen Export
422validation_failedEinige Felder sind ungültig: siehe fields
422destination_blockedDas Ziel ist gefährlich oder gesperrt
422idempotency_key_reusedDer Idempotency-Key wurde mit einem anderen Body verwendet
422token_invalidKein gültiges Entscheidungstoken für Ihr Konto
422token_expiredDas Entscheidungstoken ist älter als 10 Minuten
422webhooks_limit_reachedZu viele aktive Webhooks
429rate_limitedZu viele Anfragen: Warten Sie Retry-After ab
429too_many_exportsZu viele Exporte in Arbeit
429daily_limit_reachedNeues Free-Konto: die Links des Tages sind aufgebraucht, mehr ab 00:00 UTC
500server_errorBei uns ist etwas schiefgegangen: Versuchen Sie es später erneut
503unavailableVersuchen Sie es gleich noch einmal: siehe Retry-After
Antwort
422 Unprocessable Content
{
  "error": {
    "code": "validation_failed",
    "message": "Some fields are invalid.",
    "fields": {
      "destination": ["The destination must be an absolute http or https URL."]
    }
  }
}

Was tue ich, wenn ein Schlüssel nach außen gelangt ist?

Erstellen Sie unter «Über die API verbinden» einen neuen Schlüssel, stellen Sie Ihren Server darauf um und widerrufen Sie dann den alten: Er funktioniert sofort nicht mehr. Der Widerruf eines Schlüssels berührt weder Ihre Links noch das Protokoll oder die Webhooks.

Bewahren Sie Schlüssel in den Umgebungsvariablen Ihres Servers auf, nie im Browser, in einer mobilen App oder einem öffentlichen Repository. Könnte ein Webhook-Secret nach außen gelangt sein, schalten Sie diesen Webhook im Kundenbereich ab und erstellen Sie einen neuen.

Bereit zum Anbinden?

Registrieren Sie sich, erstellen Sie einen Schlüssel unter «Über die API verbinden» und senden Sie Ihre erste Anfrage in wenigen Minuten.

Kostenlos starten