REST-API (Tool API & Server-to-Server)
Das Browser-SDK kommuniziert mit der Tool API. Sie können dieselbe API von Ihrem eigenen Backend aus aufrufen — um Einwilligungen zu erfassen, die in Ihrer eigenen UI eingeholt wurden, um einmalige Zustimmungstoken auszustellen oder um Datensätze nachzuschlagen und zu prüfen.
Basis-URL: https://system.hashentry.com
| Methode | Pfad | Zweck | Authentifizierung |
|---|---|---|---|
GET | /tool/v1/config | Banner-Konfiguration, zugeordnete Dokumente, erkannte Region | API-Schlüssel (read) |
GET | /tool/v1/documents/{slug}/active | Aktive Version eines Dokuments, einschließlich Inhalt | API-Schlüssel (read) |
GET | /tool/v1/documents/active | Aktive Dokumente, die der Website zugeordnet sind | API-Schlüssel (read) |
POST | /tool/v1/consents | Eine Einwilligung erfassen (Route für Browser- / Mobile-SDK) | API-Schlüssel (write) + Signatur |
POST | /tool/v1/consents/batch | Bis zu 100 Einwilligungen erfassen | API-Schlüssel (write) + Signatur |
POST | /tool/v1/s2s/consents | Eine Einwilligung von Ihrem Backend aus erfassen | API-Schlüssel (write) + JWT + Signatur |
POST | /tool/v1/s2s/consents/batch | Bis zu 100 Einwilligungen von Ihrem Backend aus erfassen | API-Schlüssel (write) + JWT + Signatur |
POST | /tool/v1/s2s/approval-tokens | Ein einmaliges Token für eine Dokumentzustimmung ausstellen | API-Schlüssel (write) + JWT + Signatur |
GET | /tool/v1/consents/status | Aktuelle Einwilligungen eines Besuchers | API-Schlüssel (read) |
GET | /tool/v1/consents/verify/{id} | Integritätsprüfung eines Datensatzes | API-Schlüssel (read) |
GET POST | /api/v1/public/consents/verify | Öffentliche Integritätsprüfung (ohne Schlüssel) | keine |
Authentifizierung
Abschnitt betitelt „Authentifizierung“API-Schlüssel
Abschnitt betitelt „API-Schlüssel“Jede Anfrage an die Tool API enthält den Schlüssel der Website:
X-API-Key: he_live_…Ein Schlüssel hat einen Geltungsbereich (Scope): read erlaubt die GET-Endpunkte, write erlaubt zusätzlich POST. Die Website muss im Dashboard verifiziert sein. Hat die Website eine Liste erlaubter Ursprünge, werden Browser-Anfragen von anderen Ursprüngen abgelehnt; hat sie eine IP-Allow-List, werden Anfragen von anderen Adressen abgelehnt.
Anfragesignatur (alle POST-Anfragen)
Abschnitt betitelt „Anfragesignatur (alle POST-Anfragen)“X-Signature: <hex HMAC-SHA256>X-Timestamp: <Unix time in seconds>X-Nonce: <unique value per request>body_hash = SHA-256(raw request body) (lowercase hex)canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + body_hashsignature = HMAC-SHA256(key = API key, message = canonical) (lowercase hex)METHODwird in Großbuchstaben geschrieben.PATHist der Anfragepfad einschließlich/tool/v1, ohne den Query-String, z. B./tool/v1/s2s/consents.- Der Body-Hash wird über genau die Bytes berechnet, die Sie senden. Serialisieren Sie das JSON einmal und senden Sie genau diese Zeichenkette.
X-Timestampmuss innerhalb von ±300 Sekunden der Serverzeit liegen.X-Noncebesteht aus 8–128 Zeichen ausA–Z a–z 0–9 . _ : -(eine UUID funktioniert) und kann pro Website nur einmal verwendet werden; eine Wiederholung wird mitNONCE_REPLAYEDabgelehnt.
Durchgerechnetes Beispiel (der gemeinsame Testvektor des SDK und der API):
API key : he_live_0123456789abcdef0123456789abcdefMethod : POSTPath : /tool/v1/consentsTimestamp : 1760000000Nonce : 3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5bBody : {"consent_type":"cookie","decision":"accepted","language":"tr","idempotency_key":"9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d","metadata":{"note":"çerez ✓"}}
body_hash : 93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34canonical : POST\n/tool/v1/consents\n1760000000\n3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b\n93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34signature : e108683927badf35c36d5f467f63a9704c131d0102f14b06af7ed3046d44a8be(\n steht für ein einzelnes Zeilenvorschubzeichen; der Body ist UTF-8.) Wenn Ihre Implementierung für diese Eingaben diese Signatur erzeugt, ist sie korrekt.
const crypto = require('crypto');
function sign(apiKey, method, path, body) { const timestamp = Math.floor(Date.now() / 1000).toString(); const nonce = crypto.randomUUID(); const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex'); const canonical = [method.toUpperCase(), path, timestamp, nonce, bodyHash].join('\n'); const signature = crypto.createHmac('sha256', apiKey).update(canonical, 'utf8').digest('hex'); return { 'X-Signature': signature, 'X-Timestamp': timestamp, 'X-Nonce': nonce };}
const body = JSON.stringify({ consent_type: 'document_approval', decision: 'accepted', /* … */ });const headers = { 'Content-Type': 'application/json', 'X-API-Key': apiKey, Authorization: `Bearer ${jwt}`, // nur S2S-Routen ...sign(apiKey, 'POST', '/tool/v1/s2s/consents', body),};Die Signatur belegt, dass die Anfrage mit dem API-Schlüssel erstellt und nicht wiederholt eingespielt wurde. Da das Browser-SDK denselben Schlüssel verwendet, der im Quelltext der Seite sichtbar ist, belegt die Signatur allein nicht, dass eine Anfrage von Ihrem Backend stammt — dafür sind die S2S-Routen da.
Server-to-Server-Routen (/tool/v1/s2s/…)
Abschnitt betitelt „Server-to-Server-Routen (/tool/v1/s2s/…)“S2S-Anfragen benötigen drei Dinge: einen API-Schlüssel mit write-Scope, die Anfragesignatur und ein JWT eines Benutzers mit der Rolle api_user:
- Laden Sie im Dashboard ein Teammitglied mit der Rolle
api_userein. Diese Rolle kann nur die S2S-API verwenden; eine Anmeldung im Panel ist damit nicht möglich. - Melden Sie sich mit
POST /api/v1/auth/loginan, um ein JWT zu erhalten; erneuern Sie es mitPOST /api/v1/auth/refresh. - Senden Sie
Authorization: Bearer <jwt>zusammen mitX-API-Keyund den Signatur-Headern. Der Benutzer muss zu dem Mandanten gehören, dem die Website gehört.
Nur S2S-Anfragen dürfen ip_address, user_agent, otp_verified und otp_reference setzen. Auf der Browser-Route werden IP-Adresse und User-Agent immer aus der Verbindung übernommen, und die OTP-Felder werden ignoriert.
Eine Einwilligung erfassen
Abschnitt betitelt „Eine Einwilligung erfassen“POST /tool/v1/consents (oder /tool/v1/s2s/consents)
{ "consent_type": "cookie", "decision": "partial", "document_version_id": "0190a1b2-…", "language": "en", "categories": { "necessary": true, "analytics": true, "marketing": false }, "idempotency_key": "9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d", "visitor_id": "user-4711", "visitor_region": "DE", "compliance_framework": "gdpr", "metadata": { "order_id": "ORD-54321" }}| Feld | Regeln |
|---|---|
consent_type | Erforderlich. Bis zu 50 Zeichen (A–Z a–z 0–9 _ - .). In Verwendung: cookie, document_approval, do_not_sell_opt_out. |
decision | Erforderlich. accepted, rejected, partial oder withdrawn. |
document_version_id | UUID einer aktiven Version eines Dokuments, das der Website zugeordnet ist. Ohne sie — oder mit einer Version, die nicht aktiv ist — wird die Anfrage mit 409 CONFIG_OUTDATED abgelehnt. Eine Cookie-Einwilligung verweist auf das cookie_policy-Dokument der Website. |
language | Falls angegeben, muss es die Sprache dieser Version sein. |
categories | Objekt aus Kategorieschlüssel → Boolean. Es werden nur Kategorien gespeichert, die in der Banner-Konfiguration definiert sind. |
idempotency_key | 8–64 Zeichen (A–Z a–z 0–9 . _ : -). Siehe unten. |
visitor_id | Ihre Referenz für die Person, bis zu 100 Zeichen. |
visitor_region, visitor_country | Bis zu 10 / 2 Zeichen. |
compliance_framework | kvkk, gdpr, uk_gdpr, ccpa, lgpd, popia, pipeda, pipl, pipa oder none. |
source | Bis zu 20 Zeichen, z. B. client, server. |
metadata, extra_fields | Objekte mit jeweils höchstens 30 Schlüsseln und etwa 16 KB. |
approval_token | Einmaliges Token für eine document_approval (siehe unten). |
| Felder für Signatur / eigenes Dokument | signature_image, signature_mime_type, signer_name, signer_email, signer_phone, signer_id_number, declaration_text, drawing_duration_ms, stroke_count, strokes_data, input_type, screen_resolution, custom_document_content, custom_document_mime_type — alle auf oberster Ebene. Siehe Dokumentzustimmung & Signatur. |
ip_address, user_agent, otp_verified, otp_reference | Nur S2S. |
Die document_version_id erhalten Sie von GET /tool/v1/config?language=en (data.documents[].version.id) oder von GET /tool/v1/documents/{slug}/active?language=en.
Antwort: 202 Accepted
Abschnitt betitelt „Antwort: 202 Accepted“{ "message": "…", "data": { "consent_token": "ct_…", "proof_hash": "…64 hex characters…", "created_at": "2026-10-10T10:00:00+00:00", "duplicate": false }}202 bedeutet, dass die Anfrage alle Prüfungen bestanden hat und der Datensatz zum Schreiben angenommen wurde. Das Token, der Proof-Hash und der Zeitstempel sind zu diesem Zeitpunkt endgültig, aber die Zeile wird von einem Hintergrund-Worker geschrieben, sodass status und verify sie für einen kurzen Moment möglicherweise nicht finden.
idempotency_key
Abschnitt betitelt „idempotency_key“Senden Sie einen Schlüssel pro Benutzeraktion und verwenden Sie ihn bei jedem erneuten Versuch dieser Aktion wieder. Eine wiederholte Anfrage mit demselben Schlüssel erzeugt keinen zweiten Datensatz und verbraucht kein Kontingent; sie gibt die ursprüngliche Antwort mit "duplicate": true zurück. Schlüssel werden 24 Stunden lang gespeichert. Das Browser-SDK erzeugt eine UUID pro Entscheidung und verwendet sie für den ersten Versuch, für Wiederholungsversuche des Clients und für erneute Sendungen aus seiner Offline-Warteschlange.
POST /tool/v1/consents/batch mit { "consents": [ { … }, … ] } (1–100 Einträge, dieselben Felder wie oben).
{ "data": { "accepted": 2, "rejected": 1, "results": [ { "index": 0, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": false }, { "index": 1, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": true }, { "index": 2, "status": "rejected", "error": "…", "error_code": "CONSENT_LIMIT_EXCEEDED", "code": 429 } ] }}Der Status ist 202, wenn der Batch verarbeitet wurde; prüfen Sie jeden Eintrag von results. Validierungsfehler und ein Eintrag mit einer unbekannten oder inaktiven Dokumentversion führen zur Ablehnung des gesamten Batches (422 / 409).
Einmalige Zustimmungstoken
Abschnitt betitelt „Einmalige Zustimmungstoken“POST /tool/v1/s2s/approval-tokens — nur S2S.
{ "document_slug": "service-contract", "subject": "user-4711", "signer_name": "Jane Doe", "expires_in": 600}| Feld | Regeln |
|---|---|
document_slug / document_version_id | Mindestens eines davon. Mit einer Versions-ID gilt das Token nur für diese Version. |
subject | Erforderlich. Ihre Referenz für die Person (max. 100 Zeichen); wird als visitor_id des Datensatzes gespeichert. |
signer_name, signer_email, signer_phone, signer_id_number | Optional. Identitätsangaben, für die Sie sich verbürgen. |
otp_verified, otp_reference | Optional. Erklären eine OTP-Prüfung, die Sie durchgeführt haben. |
custom_document_hash | Optional. SHA-256 (hex) eines von Ihrem System erzeugten Dokuments; die Zustimmung muss dann genau diesen Inhalt enthalten. |
expires_in | Sekunden, 30–3600. Standard 600. |
Antwort 201:
{ "data": { "approval_token": "hat_…", "approval_token_id": "atk_…", "expires_in": 600, "expires_at": "2026-10-10T10:10:00+00:00", "document_id": "…", "document_slug": "service-contract", "document_version_id": null }}Geben Sie approval_token an die Seite weiter und übergeben Sie es dem SDK (approvalToken) oder senden Sie es als approval_token auf oberster Ebene einer document_approval-Einwilligung. Der Server verbraucht es einmal, übernimmt die Identität aus dem Token und erfasst identity_assurance: tenant_attested zusammen mit der nicht geheimen approval_token_id. Eine Anfrage, die aus einem anderen Grund abgelehnt wird (Rate-Limit, Kontingent), verbraucht das Token nicht. HashEntry speichert nur einen Hash des Tokens.
Lesen und Prüfen
Abschnitt betitelt „Lesen und Prüfen“GET /tool/v1/consents/status?visitor_id=…
Abschnitt betitelt „GET /tool/v1/consents/status?visitor_id=…“Gibt bis zu 20 nicht abgelaufene Einwilligungen einer visitor_id zurück, die neuesten zuerst. Optionale Filter: consent_type, document_slug, document_version_id.
GET /tool/v1/consents/verify/{id}
Abschnitt betitelt „GET /tool/v1/consents/verify/{id}“{id} ist eine Einwilligungs-UUID, ein consent_token (ct_…) oder ein proof_hash.
{ "valid": true, "data": { "consent_type": "cookie", "decision": "accepted", "proof_hash": "…", "hash_version": 2, "hash_valid": true, "merkle_valid": null, "is_anonymized": false, "is_tsa_stamped": false, "created_at": "2026-10-10T10:00:00+00:00" }}Was diese Antwort bedeutet — und was nicht:
valid: truebedeutet: Der Datensatz gehört zu Ihrem Mandanten, HashEntry hat den Proof-Hash aus dem gespeicherten Datensatz mit dem geheimen Schlüssel Ihres Mandanten neu berechnet und er stimmt überein, und — falls der Datensatz Teil eines Merkle-Batches ist — sein Inklusionsbeweis ist korrekt. Das bloße Vorhandensein einer Zeile reicht nicht aus.merkle_validistnull, wenn der Datensatz keinen Merkle-Beweis hat.is_tsa_stampedbesagt, dass für den Datensatz ein Zeitstempel-Token existiert (direkt oder über seinen Merkle-Batch). Es besagt nicht, dass die Signatur des Tokens in diesem Aufruf geprüft wurde.- Dies ist eine Prüfung, die HashEntry auf den Daten von HashEntry durchführt. Um zu prüfen, ohne der Antwort des Servers zu vertrauen, exportieren Sie ein Nachweispaket und prüfen Sie es selbst: Nachweise offline prüfen.
- Eine unbekannte ID liefert
404mit"valid": falseundcode: "CONSENT_NOT_FOUND".
GET|POST /api/v1/public/consents/verify
Abschnitt betitelt „GET|POST /api/v1/public/consents/verify“Kein API-Schlüssel; mit Rate-Limit. Parameter: token (ein ct_…-Token oder der Wert des Cookies _he_consent des SDK), proof_hash oder id. Zurückgegeben werden die Entscheidung, die Kategorien, die Domain der Website, Zeitstempel, der Hash-Status und Informationen zum Zeitstempel, niemals jedoch die IP-Adresse, der User-Agent oder die Besucher-ID.
- Ein vorhandener Datensatz:
200mitvalidwie oben beschrieben. - Ein Browser-Cookie-Wert, der keinem Server-Datensatz zugeordnet werden kann:
200mit"valid": false,"source": "client_token","server_record_found": falseund den dekodierten Cookie-Feldern. Das Cookie ist nicht signiert, daher wird es für sich allein nie als gültig gemeldet. - Nichts gefunden:
404mit"valid": false. Kein Bezeichner:422.
Fehlercodes
Abschnitt betitelt „Fehlercodes“Fehlerantworten sind JSON und enthalten in code einen stabilen Maschinencode (außerdem error_code und error.code; manche Authentifizierungsfehler verwenden nur error.code).
| HTTP | Code | Bedeutung |
|---|---|---|
| 401 | MISSING_API_KEY, INVALID_API_KEY | Kein oder unbekannter API-Schlüssel. |
| 401 | MISSING_SIGNATURE_HEADERS, TIMESTAMP_EXPIRED, INVALID_SIGNATURE, INVALID_NONCE, NONCE_REPLAYED | Probleme mit der Anfragesignatur. |
| 401 | MISSING_BEARER_TOKEN, INVALID_BEARER_TOKEN | S2S: JWT fehlt oder ist ungültig. |
| 403 | SITE_NOT_VERIFIED | Verifizieren Sie die Website im Dashboard. |
| 403 | ORIGIN_NOT_ALLOWED, IP_NOT_ALLOWED | Nicht in der Allow-List der Website. |
| 403 | INSUFFICIENT_ROLE, TENANT_MISMATCH | S2S: Der Benutzer ist kein api_user dieses Mandanten. |
| 403 | — | Der Scope des API-Schlüssels erlaubt die Methode nicht. |
| 404 | DOCUMENT_NOT_FOUND, VERSION_NOT_FOUND, CONSENT_NOT_FOUND | |
| 409 | CONFIG_OUTDATED | document_version_id fehlt, ist nicht aktiv, hat die falsche Sprache, oder der Konfigurations-Cache des Servers ist kalt. Lesen Sie /tool/v1/config erneut; senden Sie nur dann erneut, wenn die Version weiterhin die aktive ist. |
| 422 | VALIDATION_ERROR | Feldfehler stehen in errors. |
| 422 | APPROVAL_TOKEN_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_CONFLICT | Siehe einmalige Zustimmungstoken. |
| 422 | INVALID_CATEGORIES, METADATA_TOO_MANY_KEYS, METADATA_TOO_LARGE, EXTRA_FIELDS_TOO_MANY_KEYS, EXTRA_FIELD_NAME_TOO_LONG, EXTRA_FIELDS_TOO_LARGE, STROKES_DATA_TOO_LARGE, INVALID_SIGNATURE_IMAGE, SIGNATURE_IMAGE_TOO_LARGE, INVALID_CUSTOM_DOCUMENT, UNSUPPORTED_CUSTOM_DOCUMENT_TYPE, CUSTOM_DOCUMENT_TOO_LARGE | Payload-Limits. |
| 429 | RATE_LIMIT_EXCEEDED | Rate-Limit je Tarif. Siehe Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining. |
| 429 | CONSENT_LIMIT_EXCEEDED, NO_CONSENT_SUBSCRIPTION | Das Einwilligungskontingent des Tarifs ist aufgebraucht / es gibt keinen aktiven Tarif. |
| 429 | TOO_MANY_INVALID_API_KEYS | Zu viele Anfragen mit ungültigen Schlüsseln von einer IP-Adresse. |
| 503 | QUEUE_OVERLOADED, ADMISSION_UNAVAILABLE, API_KEY_LOOKUP_UNAVAILABLE | Vorübergehend. Versuchen Sie es nach Retry-After mit demselben idempotency_key erneut. |
Wiederholen Sie Antworten mit 408, 425, 429 und 5xx mit demselben Body und demselben idempotency_key (und mit neuem Zeitstempel, neuer Nonce und neuer Signatur). Wiederholen Sie andere 4xx-Antworten nicht unverändert.