API-Referenz
Ergänzen Sie Registrierung, Anmeldung oder Checkout um Netzwerk- und Geräteprüfungen. Ihr Backend erhält die Signale; Sie entscheiden über die Reaktion.
Zuletzt aktualisiert am 18. September 2026 · jede API-Änderung landet im Changelog · Stabilitätsrichtlinie: nur additive Änderungen
sk_test_sandbox-Schlüssel — kein Konto erforderlich.Playbooks je Angriff: Kontoübernahme, Card Testing, Bonusmissbrauch, und mehr →
Schnellstart
Von null zur funktionierenden Integration in 5 Minuten. Erstellen Sie einen kostenlosen Schlüssel unter /signup — 10.000 Besucherprüfungen im Monat im kostenlosen Tarif, keine Karte.
Sie nutzen einen KI-Coding-Assistenten? Fügen Sie diesen Prompt ein, und er baut Maskbreak Ende-zu-Ende in Ihre App ein — Frontend-Script, Backend-Prüfung, Umgebungsvariable und ein Test. Er folgt der maschinenlesbaren Anleitung unter maskbreak.com/integrate.md.
Fetch https://maskbreak.com/integrate.md and follow it to add Maskbreak fraud protection to this app — protect signup, login, and checkout. My API key is sk_live_YOUR_API_KEY; put it in a MASKBREAK_API_KEY env var, never in client-side code. Then show me how to test it.
Lädt…
Das Beispiel unten setzt die Entscheidung durch. Protokollieren Sie beim ersten Deployment jede Entscheidung und lassen Sie jeden Besucher weiter. Wechseln Sie in den Durchsetzungsmodus, sobald die Ereignisse im Dashboard stimmig aussehen. Der Integrationsleitfaden enthält einen Handler mit dem Schalter MASKBREAK_MODE: standardmäßig watch, enforce, sobald Sie bereit sind.
// STEP 1 — HTML. Load the collector before your form handler. <script defer src="https://maskbreak.com/assets/sentinel.js"></script> <form class="monocle-enriched" id="login-form"> <input type="email" name="email" required> <button type="submit">Continue</button> </form> // STEP 2 — Your frontend script, after the SDK loads. document.getElementById('login-form').addEventListener('submit', async (event) => { event.preventDefault(); const { token, fingerprintEventId, tz } = await window.Sentinel.collect(); const response = await fetch('/your-login-endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: event.target.email.value, token, fingerprintEventId, tz }) }); // Handle response.status and the next step in your existing login UI. }); // STEP 3 — Node 22 / Express backend. // express, app and existingLoginHandler belong to your application. // This check is NOT authentication and never creates a login session. app.use(express.json({ limit: '16kb' })); app.post('/your-login-endpoint', async (req, res, next) => { try { const { token, fingerprintEventId, tz, email } = req.body; const response = await fetch('https://maskbreak.com/v1/evaluate', { method: 'POST', signal: AbortSignal.timeout(2000), // example budget; measure your own headers: { 'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ token, fingerprintEventId, tz, email }) }); if (!response.ok) throw new Error('Visitor check unavailable'); const result = await response.json(); // Production gate: never authorize from a fixture or personal test key. // Use a live key in MASKBREAK_API_KEY; test the integration separately. if (result.test || result.sandbox || result.sample) { throw new Error('Synthetic visitor check'); } if (!['allow', 'review', 'block'].includes(result.decision)) { throw new Error('Unexpected verdict'); } if (result.decision === 'block') { return res.status(403).json({ error: 'Action refused by policy' }); } if (result.decision === 'review' || result.degraded) { // Implement an actual step-up flow; never mint a session here. return res.status(409).json({ next: 'step_up' }); } req.fraudCheck = result; return next(); // normal credential + authorization checks still run } catch { // Example temporary hold. Choose a fallback for each business action. return res.status(503).json({ error: 'Check unavailable; try again shortly' }); } }, existingLoginHandler);
Bevor Sie eine Entscheidung durchsetzen
Testen Sie normale Entscheidungen, fehlende Daten und die Wiederaufnahme unterbrochener Kundenvorgänge getrennt. Diese Leitfäden behandeln die Entscheidungen auf Anwendungsseite, die Ihnen eine API-Antwort nicht abnehmen kann.
Live ausprobieren
Rufen Sie den Sandbox-Endpunkt mit einem Klick auf. Keine Registrierung, kein API-Schlüssel — nur echte /v1/evaluate-Antwortstrukturen, die Sie in Ihren Code übernehmen können.
Produktionsaufrufe nutzen POST /v1/evaluate mit einem token aus dem SDK und einem Authorization: Bearer sk_live_...-Header. Der Beispiel-Endpunkt liefert dieselbe Antwortstruktur, sodass Sie Ihre Parsing-Logik vor der Registrierung verdrahten können.
So funktioniert es
Maskbreak nutzt einen dreistufigen Ablauf Client → Ihr Backend → Maskbreak-API, damit Ihr geheimer Schlüssel nie im Browser landet.
Das Maskbreak-SDK läuft unsichtbar im Browser Ihres Nutzers und sammelt Telemetrie. Es fügt Ihren Formularen ein verschlüsseltes token hinzu.
Ihr Frontend sendet das Token zusammen mit den übrigen Formulardaten an Ihren eigenen Backend-Server.
Ihr Backend ruft POST /v1/evaluate mit Ihrem geheimen Schlüssel auf. Maskbreak liefert sofort einen Threat-Intelligence-Bericht.
Authentifizierung
Alle Anfragen an /v1/evaluate müssen Ihren geheimen API-Schlüssel als Bearer-Token enthalten. Ihren Schlüssel finden Sie im Dashboard.
sk_live_… niemals in Ihr HTML, JavaScript oder anderen clientseitigen Code. Verwenden Sie ihn nur in Ihrer Backend-Serverumgebung.Schlüssel rotieren
Rotieren Sie in der Konsole (Integration → Rotieren, oder Einstellungen → API-Schlüssel; Ihr Passwort wird erneut abgefragt). Der neue Schlüssel funktioniert sofort, und der bisherige bleibt 24 Stunden gültig, damit ein Deployment ohne Lücke umstellen kann. Ist ein Schlüssel geleakt, beendet Alten Schlüssel jetzt widerrufen in den Einstellungen diese Frist sofort. Während der Frist zählen beide Schlüssel gegen dasselbe Stundenkontingent, und /v1/usage meldet für beide dieselben Werte.
Jedes Konto hat einen persönlichen sk_test_…-Schlüssel. Er führt die Live-Pipeline aus und speichert Testereignisse, ohne Live-Webhooks zu senden; seine Prüfungen zählen zu Ihrem Monatskontingent, er hat ein eigenes Stundenkontingent und umgeht die IP-Allowlist. Der öffentliche sk_test_sandbox-Schlüssel beantwortet ausschließlich deterministische Test-Token, ohne Konto und ohne gespeicherte Ereignisse. Eine IP-Allowlist für den Live-Schlüssel beschränkt /v1/evaluate, /v1/lookup und das Ausstellen eines Tokens über /oauth/token (400 unauthorized_client von jeder anderen Adresse), nicht /v1/usage.
OAuth 2.0 Client Credentials Neu
Tauschen Sie Ihre Konto-E-Mail und Ihren API-Schlüssel über den Standard-Grant client_credentials gegen ein kurzlebiges Bearer-Token. Das Token trägt Ihre Konto-ID, nie den Schlüssel selbst. Es gilt 60 Minuten, authentifiziert als Ihr aktueller Schlüssel seines Typs und ist an den Schlüssel gebunden, mit dem es ausgestellt wurde: Nach einer Rotation funktioniert es weiter, solange dieser Schlüssel gilt (die 24-Stunden-Frist des bisherigen Schlüssels); der Widerruf des bisherigen Schlüssels, eine zweite Rotation oder die Rotation des Testschlüssels beenden es sofort mit 401 — stellen Sie dann ein neues aus. Ein mit Ihrem sk_test_-Schlüssel ausgestelltes Token bleibt ein Test-Credential. API-Schlüssel funktionieren genau wie zuvor; das hier ist additiv.
POST /oauth/token
/.well-known/oauth-authorization-server und /.well-known/oauth-protected-resource.curl -X POST https://maskbreak.com/oauth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"client_credentials","client_id":"your@email.com","client_secret":"sk_live_..."}'Verwenden Sie das zurückgegebene access_token überall dort im Authorization: Bearer-Header, wo ein API-Schlüssel akzeptiert wird. Token laufen ab; erneuern Sie sie durch Wiederholen des Austauschs. Der Schlüssel selbst verlässt Ihren Server nie.
SDK einbinden
Binden Sie das Maskbreak-SDK auf jeder Seite ein, auf der Sie Nutzer auswerten wollen. Ein Script lädt beide Erkennungsschichten — Netzwerkintelligenz (VPN, Proxy, Rechenzentrum) und Geräteintelligenz (Antidetect-Browser, Bots, Manipulation) — und fügt beide Token in Ihre Formulare ein.
<!-- Maskbreak SDK — network + device, paste inside <head> --> <script async src="loading..."></script> <!-- Add class="monocle-enriched" to any form you want evaluated --> <form class="monocle-enriched" id="my-form"> <!-- The SDK injects both automatically: --> <input type="hidden" name="monocle" value="eyJ..." /> // network <input type="hidden" name="sentinel_fp" value="a1b2..." /> // device </form>
Das SDK braucht etwa 1–2 Sekunden zum Laden und Erzeugen der Token. Wenn die Geräteschicht nicht laufen kann (z. B. weil ein gehärteter Browser sie blockiert), läuft die Auswertung allein über die Netzwerkschicht weiter.
Token einsammeln
Rufen Sie beim Absenden Sentinel.collect() auf — es wartet, bis die Geräteschicht fertig ist (höchstens 5 Sekunden; collect({ timeout: ms }) ändert das), und liefert { token, fingerprintEventId }. Leiten Sie beides an Ihr Backend weiter. (Oder lesen Sie die versteckten Felder monocle und sentinel_fp direkt aus.) Clientseitig gerenderte App (React, Next.js, Vue)? Es ist nichts weiter nötig: Das Skript hält ein verstecktes Formular bereit, sodass das Token auch für Formulare da ist, die Ihre App nach dem Laden der Seite rendert.
document.getElementById('my-form').addEventListener('submit', async (e) => { e.preventDefault(); // Both layers: network token + device event id const { token, fingerprintEventId } = await window.Sentinel.collect(); if (!token) { // Network SDK still loading — ask user to try again return showError('Security check loading. Please try again.'); } // Forward both to your backend with the rest of the form data const res = await fetch('/your-backend-endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: e.target.email.value, token, fingerprintEventId // ← both layers to your backend }) }); });
Ihre Website sendet eine Content-Security-Policy? Ergänzen Sie Ihre Direktiven um die eigenen Hosts des SDK: script-src https://maskbreak.com https://mcl.maskbreak.com https://*.mcl.maskbreak.com https://mesu.maskbreak.com 'wasm-unsafe-eval', connect-src https://mcl.maskbreak.com https://*.mcl.maskbreak.com wss://mcl.maskbreak.com wss://*.mcl.maskbreak.com https://mesu.maskbreak.com und worker-src https://mesu.maskbreak.com blob:. Ein Teil, den die Richtlinie blockiert, bleibt leer, und die Prüfung läuft ohne ihn weiter.
Den Evaluate-Endpunkt aufrufen
lädt…
Rufen Sie das von Ihrem Backend-Server auf — nie aus dem Browser. Übergeben Sie das Token vom Client und Ihren geheimen API-Schlüssel.
Request-Body
Sentinel.collect() oder input[name="monocle"]. Senden Sie es für die vollständige Netzwerkanalyse. Bei direkten HTTP-Anfragen wird ein fehlendes oder leeres Token als Auswertung mit eingeschränkter Netzwerkanalyse akzeptiert; ein Token, das kein String ist oder vom Anbieter abgelehnt wurde, führt zu 400. Die aktuellen Server-SDKs für Node, Python und PHP verlangen bereits lokal ein nicht leeres Token; verwenden Sie daher für diesen Fallback direkte HTTP-Anfragen. Senden Sie den Body als application/json; ein JSON-Objekt mit einem anderen oder fehlenden Content-Type oder ein Formular-Body (token=…&fingerprintEventId=…) wird genauso gelesen.sentinel_fp ein; leiten Sie sie hier weiter (beide Namen werden akzeptiert), um die device.*-Signale freizuschalten (Antidetect, Automatisierung, Emulator, …). Ohne sie erhalten Sie ein reines Netzwerk-Urteil.fingerprintEventId zählt Maskbreak, mit wie vielen verschiedenen Konten dieses Gerät unter Ihrem API-Schlüssel verknüpft wurde, und liefert device.linked_accounts / device.multi_account — Multi-Accounting-Erkennung ohne zusätzliche Integration. Nur als Einweg-Hash gespeichert.email.disposable hinzu — geprüft gegen einen laufend aktualisierten Feed mit Tausenden Wegwerf-Domains. Ein Treffer fügt den Grund disposable_email hinzu, erhöht den risk_score und stuft allow zu review hoch. Die Adresse wird nur vorübergehend geprüft und nie gespeichert oder protokolliert.Europe/Tallinn. Unser Client-SDK sendet sie automatisch als sentinel_tz, was dieser Endpunkt ebenfalls akzeptiert; selbst auslesen können Sie sie mit Intl.DateTimeFormat().resolvedOptions().timeZone — ohne Berechtigungsabfrage und ohne Geolokalisierung. Fügt der Antwort einen timezone-Block hinzu. Sieht die Verbindung gewöhnlich aus, während die Uhr des Browsers zu einem anderen Land gehört, ist das die Form eines Residential-Proxys, den die Netzwerkschicht nicht erwischt hat: Es fügt den Grund timezone_mismatch hinzu, erhöht den risk_score und stuft allow zu review hoch. Bewusst nicht ausgelöst bei VPN, Proxy, Tor oder Rechenzentrums-Exit — der Uhr zu widersprechen ist genau das, was die tun — und nie allein genug für block, weil Reisende und Auswanderer hier legitim landen. Leiten Sie das von Sentinel.collect() zurückgegebene tz in Ihrer direkten HTTP-Anfrage weiter. Die aktuellen Hilfsfunktionen der Server-SDKs serialisieren tz nicht; verwenden Sie direkte HTTP-Anfragen, wenn Sie dieses Feld benötigen.Testen ohne Browser: Senden Sie ein deterministisches Test-Token — test_clean, test_vpn, test_proxy, test_datacenter oder test_tor —, um Ihre allow-/review-/block-Behandlung Ende-zu-Ende zu prüfen. Testaufrufe werden wie echte authentifiziert und ratenlimitiert, aber nie auf Ihr Monatskontingent angerechnet, gespeichert oder per Webhook gemeldet, und die Antwort trägt "test": true.
CI & Staging ohne Konto: Der öffentliche Sandbox-Schlüssel sk_test_sandbox akzeptiert dieselben test_*-Token und liefert dieselben deterministischen Strukturen — keine Registrierung, nichts berechnet, nichts gespeichert, belastet nie ein echtes Kontingent. Er beantwortet nur Test-Token (Live-Traffic braucht weiterhin Ihren echten Schlüssel) und ist pro IP ratenlimitiert. curl -X POST https://maskbreak.com/v1/evaluate -H "Authorization: Bearer sk_test_sandbox" -H "Content-Type: application/json" -d '{"token":"test_vpn"}'
Ihr eigener Testschlüssel (volle Pipeline): Jedes Konto hat zusätzlich einen persönlichen sk_test_…-Schlüssel (Einstellungen → API-Schlüssel), der anders als die Sandbox die komplette Live-Pipeline ausführt — echte Token, echte Geräteintelligenz, Ihre Regeln und Ausnahme-Pins eingeschlossen. Die Events, die er erzeugt, sind in der Konsole als Test markiert, aus Ihrer Statistik ausgenommen, lösen nie Webhooks aus, und die Antwort trägt "test": true. Seine Prüfungen zählen wie die Prüfungen der Konsole zu Ihrem Monatskontingent; die festen Test-Token oben nicht. Er hat sein eigenes Stundenkontingent und ist bewusst von der IP-Allowlist ausgenommen, damit CI und Laptops die Pipeline ausprobieren können, ohne Löcher in Ihre Produktionsbeschränkung zu schlagen.
const response = await fetch('https://maskbreak.com/v1/evaluate', { method: 'POST', headers: { 'Authorization': 'Bearer sk_live_YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ token: req.body.token, // as sent by your frontend (see Quick Start Step 2) fingerprintEventId: req.body.fingerprintEventId, tz: req.body.tz }) }); const data = await response.json(); // data.decision → 'allow' | 'review' | 'block' (covers VPN, proxy, // datacenter, Tor, antidetect, automation) — route on this // data.reasons → machine-readable why if (data.decision === 'block') { return res.status(403).json({ error: 'Suspicious connection.' }); }
# pip install sentinelsup from sentinel import Sentinel s = Sentinel(api_key='sk_live_YOUR_API_KEY') # or omit — reads SENTINEL_KEY env var # request_body is the JSON body parsed by your web framework. # This SDK requires a non-empty token; use raw HTTP for degraded fallback or tz. result = s.evaluate( token=request_body.get('token', ''), fingerprint_event_id=request_body.get('fingerprintEventId') ) # result.decision → 'allow' | 'review' | 'block' — route on this # result.reasons → machine-readable why if result.is_blocked: return '403 Suspicious connection', 403
curl -X POST 'https://maskbreak.com/v1/evaluate' \ -H 'Authorization: Bearer sk_live_YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{"token": "YOUR_BROWSER_TOKEN", "fingerprintEventId": "YOUR_DEVICE_EVENT", "tz": "Europe/Tallinn"}'
// composer require sentinelsup/sdk $sentinel = new \Sentinel\Client(); // or pass the key — reads SENTINEL_KEY env var // The frontend sends JSON, not a URL-encoded form. // This SDK requires a non-empty token; use raw HTTP for degraded fallback or tz. $input = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR); $result = $sentinel->evaluate([ 'token' => $input['token'] ?? '', 'fingerprintEventId' => $input['fingerprintEventId'] ?? null, ]); // $result->decision → 'allow' | 'review' | 'block' — route on this // $result->reasons → machine-readable why if ($result->isBlocked()) { http_response_code(403); exit(); }
Wiederkehrende Geräte und Kontoverknüpfungen
Senden Sie ein frisches, echtes Browser-Ereignis mit einem Live-Schlüssel an /v1/evaluate. device.customer_history zählt unterschiedliche verifizierte Ereignisse nur für Ihr Kundenkonto in den letzten 90 Tagen: erster und letzter Besuch, Wiederkehr und Anzahl zugehöriger Konten. Das Dashboard zeigt unter Ereignisse → Untersuchen die 50 zuletzt gesehenen Geräte. Die Historie beginnt mit Einführung dieser Funktion; alte Ereignisse werden nicht nachgetragen. Wiederholungen desselben Ereignisses zählen einmal. Fehlende Gerätedaten bedeuten keine Historie, nicht ein neues oder sicheres Gerät.
Für Kontoverknüpfungen muss Ihr Backend accountId aus seiner eigenen authentifizierten Sitzung bestimmen, niemals eine behauptete Konto-ID aus dem Browser übernehmen. Verwenden Sie eine pseudonyme ID, keine E-Mail-Adresse. Geteilte Geräte liefern Kontext, keinen Betrugsnachweis. Testschlüssel tragen nicht zu dieser neuen Historie bei. Die bisherigen Felder times_seen, returning und first_seen behalten ihre Bedeutung als gemeinsamer Zähler; verwenden Sie customer_history für Ihre eigenen Besucher.
// Your backend: accountId comes from YOUR authenticated session.
const response = await fetch('https://maskbreak.com/v1/evaluate', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.MASKBREAK_API_KEY,
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(8000),
body: JSON.stringify({
token: req.body.token,
fingerprintEventId: req.body.fingerprintEventId,
accountId: req.user ? String(req.user.id) : undefined,
gpuEvidence: req.body.gpuEvidence // optional beta; otherwise omit
})
});
if (!response.ok) throw new Error('Visitor check unavailable');
const result = await response.json();
const history = result.device?.customer_history;
// history: {visits, returning, first_seen, last_seen, accounts, window_days:90}
// Use your existing decision/review policy. History is not authorization.Optionale WebGPU-Ähnlichkeitsbeta
Ein Administrator kann die Beta in den Einstellungen unter „Experimentell: GPU-Ähnlichkeit“ aktivieren. Konfigurieren Sie zuerst Ihre Website im Dashboard. Erfassen Sie dort das normale SDK-Ereignis und fordern Sie über Ihr Backend eine Challenge an. Erforderlich ist ein vom Anbieter verifiziertes Ereignis der konfigurierten Website, höchstens fünf Minuten alt. Die Challenge gilt zwei Minuten und ist an Kunde, Gerät und Ereignis gebunden. Das normale SDK startet keine GPU-Erfassung. Der separate Collector benötigt HTTPS und worker-src blob: in Ihrer CSP.
// 1. Your backend requests a challenge using its LIVE API key.
// Protect your own route with session/CSRF checks and rate limits.
const response = await fetch('https://maskbreak.com/v1/device-evidence/challenge', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.MASKBREAK_API_KEY,
'Content-Type': 'application/json' },
signal: AbortSignal.timeout(6000),
body: JSON.stringify({ fingerprintEventId: req.body.fingerprintEventId })
});
if (!response.ok) return res.status(200).json({ status: 'unavailable' });
return res.json(await response.json()); // challenge, expires_at, protocol
// 2. Browser, after your required consent/notice flow:
// Load https://maskbreak.com/assets/device-evidence.js?v=1 separately.
// Pass the challenge from YOUR backend; never send your API key here.
const gpuEvidence = challenge
? await MaskbreakGPU.collect(challenge)
: { status: 'unavailable' };
// Forward only successful evidence with the SAME fingerprintEventId:
// gpuEvidence: gpuEvidence.features ? gpuEvidence : undefined
// If unavailable or your challenge route fails, continue normal checks.Verwenden Sie für die Beta direkte HTTP-Anfragen; veröffentlichte Server-SDKs können gpuEvidence auslassen. Limits: 60 Challenges pro Kunde und Stunde, 50 offen, ein akzeptiertes GPU-Muster pro verifiziertem Ereignis und 30 Sekunden Pause pro Gerät. Die Erfassung endet nach sechs Sekunden; nicht unterstützte oder blockierte Browser liefern unavailable. Gespeichert wird höchstens der letzte verschlüsselte Merkmalsvektor von 200 Geräten, bis 30 Tage nach dem letzten akzeptierten Muster. Deaktivieren löscht Muster und Challenges.
device.gpu_evidence meldet bei verarbeitbaren Belegen recorded, disabled, invalid, expired_or_used, not_saved oder unavailable. Das nächste Gerät und die RMS-Distanz zeigen nur experimentelle Ähnlichkeit: kleiner bedeutet ähnlicher, aber kein Grenzwert beweist Identität. Ähnliche GPUs können sich ähneln, Einstellungen Ergebnisse verändern und Browserdaten gefälscht werden. GPU-Belege verändern keine Scores, blockieren niemanden und verknüpfen keine Konten. Administrator-Zustimmung ist keine Besucher-Einwilligung: Rechtsgrundlage klären, Erfassung erklären und erforderliche Einwilligung einholen. Beginnen Sie mit freigegebenen Testgeräten.
Experimentelle Browser-Konsistenzprüfung
Nutzen Sie die bestehende GPU-Beta-Freigabe und die ereignisgebundene Challenge für eine optionale Diagnose im Prüfschritt. Sie vergleicht Browser-/Betriebssystemfamilien von Seite und Worker, allgemeine WebGL-/WebGPU-Anbieter sowie zwei neue Berechnungsergebnisse. Senden Sie browserEvidence per HTTP mit einem Live-Schlüssel; das normale SDK startet diese Prüfung nicht.
// Browser: load /assets/browser-consistency.js?v=1 from maskbreak.com.
// Run only on an opted-in review step, after required notice/consent.
// Obtain the one-use challenge from YOUR authenticated backend route above.
const browserEvidence = challenge
? await MaskbreakBrowserConsistency.collect(challenge)
: { status: 'unavailable' };
// Forward to YOUR backend with the same session-bound fingerprintEventId.
// Do not expose a Maskbreak API key in browser code.
// Backend: optional field in the existing POST /v1/evaluate JSON body:
// browserEvidence: browserEvidence.protocol ? browserEvidence : undefined
// Send either browserEvidence OR gpuEvidence for a device event, not both.
// Read result.device?.browser_consistency:
// {status:'inconsistent', experimental:true, source:'browser_reported',
// affects_decision:false, findings:['browser_context'], checks:[...]}
// Keep the original decision/review policy. This report is not authorization.Die API liefert benannte Prüfungen und consistent, inconsistent oder inconclusive oder einen Fehler-, Deaktivierungs- oder Wiederholungsstatus. Befunde werden serverseitig neu berechnet, bleiben aber fälschbare Browserangaben, kein Hardware-Nachweis. Andere GPU-Anbieter, verborgene Daten, Software-Rendering oder fehlendes WebGPU sind keine Risikosignale. Wiederholte Widersprüche können zur Untersuchung eines bereits prüfbedürftigen Besuchs beitragen; Scores, Entscheidungen, Sperren oder Identitätsverknüpfungen ändern sich nicht automatisch. Rohberichte und Zusammenfassungen werden nicht gespeichert und erscheinen nicht im Ereignisdetail. Vor der Verwendung in Regeln mit freigegebenen Geräten testen.
Antwortobjekt
Ein erfolgreicher Aufruf liefert 200 OK mit dieser JSON-Struktur.
| Feld | Typ | Beschreibung |
|---|---|---|
| decision | string | Empfohlene Aktion: "allow", "review" oder "block". Nur beratend — die Richtlinie legen Sie fest. Das reasons-Array erklärt jede Entscheidung, sodass Sie sie protokollieren oder Ihrem Endnutzer einen Ablehnungsgrund zeigen können. Wenn Sie in Ihrem Dashboard eigene Regeln konfigurieren, liefert dieses Feld Ihre Aktion für ein passendes Signal (die strengste Regel gewinnt). |
| engine_decision | string | decision_source wird immer dann zurückgegeben, wenn Ihre Richtlinie auf dieses Event gepasst hat: "rules" (mit rule_matched, das die Signale nennt, die eine Regel ausgelöst haben) oder "exception" (mit exception_matched, das den IP-/Besucher-Pin aus der Ausnahmenliste Ihres Dashboards nennt — explizite Pins schlagen Signalregeln). engine_decision erscheint zusätzlich, wenn der Treffer die Entscheidung tatsächlich verändert hat, und bewahrt das eigene Risiko-Score-Urteil der Engine. |
| risk_score | integer | Zusammengesetzter Risiko-Score 0–100 über alle Netzwerk- und Gerätesignale. Wird von eigenen Regeln nicht verändert. |
| isSuspicious | boolean | Legacy-Komfortflag. true, wenn VPN, Proxy, Tor, Browser-Manipulation (Antidetect), Automatisierung, Emulator oder virtuelle Maschine ausgelöst haben. Ein Rechenzentrums- oder Anonymisierungssignal allein erhöht den risk_score, setzt dieses Flag aber nicht — routen Sie für volle Abdeckung über decision. |
| ip | string | Die entlarvte, echte IP-Adresse des Nutzers. Gespiegelt unter details.ip. |
| country | string | ISO-3166-1-Ländercode (z. B. "US", "EE"). Gespiegelt unter details.cc. |
| network. |
boolean | Kommerzielles VPN erkannt (NordVPN, Proton, ExpressVPN usw.). Gespiegelt unter details.vpn. |
| network. |
boolean | Residential- oder SOCKS/HTTP-Proxy erkannt. Gespiegelt unter details.proxied. |
| network. |
boolean | Traffic aus einem bekannten Rechenzentrums-/Cloud-Anbieter-ASN. Gespiegelt unter details.dch. |
| network. |
boolean | Tor-Exit-Node erkannt. Gespiegelt unter details.tor. |
| network. |
boolean | Irgendeine Anonymisierungsschicht erkannt. Gespiegelt unter details.anon. |
| network. |
boolean | true, wenn weder Rechenzentrum noch Proxy ausgelöst haben (sieht nach einer echten Heim-IP aus). |
| network. |
string | Identifizierter Anbieter, wenn bekannt (z. B. "PROTON_VPN", "BRIGHT_DATA"). Gespiegelt unter details.service. |
| network. |
boolean | Wie gut die Netzwerkmessung war. complete ist false bei einer unvollständigen Messung (der Besucher hat abgeschickt, bevor die Netzwerkprüfung fertig war; höheres Risiko einer falschen Antwort) und wenn es gar keine Messung gab. sdk_blocked ist true, wenn ein Token ankam, die Prüfung den Besucher aber nicht messen konnte: Die gemeldete Adresse ist privat, fehlt oder gehört zur Infrastruktur der Netzwerkprüfung selbst. So sieht eine blockierte oder entfernte Messung aus. Dann sind die Netzwerkflags kein Beleg für eine unauffällige Verbindung. Beide Felder gibt es bei Live-Auswertungen (nicht bei Test-Tokens, in der Sandbox oder bei Beispielen); sie sind nur Information und ändern decision nie. |
| device. |
boolean | Antidetect-/Fingerprint-Spoofing-Browser (Multilogin, Kameleo, GoLogin usw.). Nur vorhanden, wenn fingerprintEventId gesendet wurde. |
| device. |
boolean | Automatisierter Browser erkannt (Puppeteer, Playwright, Selenium). |
| device. |
boolean | Mobil-Emulator. |
| device. |
boolean | Der Browser läuft in einer virtuellen Maschine. Allein ergibt das review (seit 29. September 2026). |
| device. |
boolean | Privater / Inkognito-Modus erkannt. |
| device. |
boolean | Erweiterte Datenschutzeinstellungen aktiv (z. B. Brave Shields, Firefox resist-fingerprinting). |
| device. |
boolean | Die Besucher-IP steht auf E-Mail-Spam- oder Angriffsquellen-Blocklisten. |
| device. |
boolean | Dieses Gerät wird ungewöhnlich oft identifiziert (High-Activity Device). Informativ — häufige Besuche allein sind kein Betrug, aber in Kombination mit anderen Signalen deuten sie oft auf Automatisierung oder Farming hin. Fügt den Grund high_activity_device hinzu. |
| device. |
string | Stabiler Geräte-Fingerprint-Hash. Dasselbe Gerät liefert über Sitzungen hinweg dieselbe ID, auch nach dem Löschen der Cookies — nützlich für ATO-Abwehr und Geräte-Clustering. |
| device. |
number | Manipulations-Score der Datenquelle von 0 bis 1 oder 0, wenn nicht verfügbar. Keine Betrugswahrscheinlichkeit und kein Nachweis eines unveränderten Browsers. Nutzen Sie checked_signals und antidetect, um das Ergebnis einzuordnen. |
| device. |
boolean / string[] | complete bedeutet, dass Browser-Manipulations- und Bot-Prüfungen Ergebnisse geliefert haben, nicht dass jede optionale Prüfung lief. checked_signals listet tatsächlich geprüfte boolesche Gerätefelder. Ungeprüfte Felder bleiben aus Kompatibilitätsgründen false; das ist kein unauffälliges Prüfergebnis. Die Entscheidungsregeln bleiben unverändert. Ältere Antworten und Demo-Daten können diese Angaben auslassen; fehlende Angaben beweisen keine erfolgte Prüfung. |
| device. |
integer | Kumulierte Sichtungen des Geräte-Hashes, der mit einer Einwegfunktion gebildet wurde, solange sein Verlaufsdatensatz existiert. Dies ist weder die Anzahl Ihrer Kunden noch auf Ihren Schlüssel beschränkt und auch kein Zähler für ein gleitendes 90-Tage-Fenster. Datensätze können nach 90 Tagen ohne Sichtung bereinigt werden. Ein Wert von 1 ist ein begrenzter Hinweis auf ein neues Gerät, kein Beweis für Betrug. |
| device. |
string | ISO-8601-Zeitpunkt der ersten Sichtung im aktuellen Geräteverlaufsdatensatz. Dieser Verlauf ist nicht auf Ihren API-Schlüssel beschränkt und kann nach einer Bereinigung wegen Inaktivität neu beginnen. Verwenden Sie Ihre eigene Kontohistorie, um zu bestimmen, wie lange jemand bereits Ihr Kunde ist. Das separate Feld linked_accounts bleibt auf Ihr Maskbreak-Kundenkonto beschränkt. |
| device. |
boolean | True, wenn times_seen > 1 — dieses Gerät wurde schon einmal ausgewertet. |
| device. |
integer | Mit wie vielen verschiedenen Konten dieses Gerät in den letzten 90 Tagen unter Ihrem API-Schlüssel gesehen wurde. Nur vorhanden, wenn Sie Ihre eigene accountId zusammen mit fingerprintEventId in der Anfrage übergeben. Beide Kennungen werden nur als Einweg-Hashes gespeichert. |
| device. |
boolean | True, wenn linked_accounts > 1 — das zentrale Multi-Accounting-Signal (Bonusmissbrauch, Testphasen-Missbrauch, doppelte Registrierungen). Fügt den Grund multi_account_device hinzu. |
| email. |
boolean | Nur vorhanden, wenn die Anfrage den optionalen Parameter email enthielt. true, wenn die Adresse eine bekannte Wegwerf-Domain nutzt — fügt den Grund disposable_email hinzu und stuft allow zu review hoch. |
| timezone. |
string | Nur vorhanden, wenn die Anfrage eine erkannte tz enthielt. Die vom Browser gemeldete IANA-Zone, zurückgespiegelt. |
| timezone. |
string | ISO-3166-1-alpha-2-Land, zu dem diese Zone gehört. |
| timezone. |
boolean | true, wenn das Land der Zone gleich country (dem der Exit-IP) ist. Bauen Sie darauf Ihre eigene Regel, wenn Sie eine strengere Richtlinie als unsere wollen. |
| timezone. |
boolean | false bei VPN-, Proxy-, Tor- oder Rechenzentrums-Exit, wo eine Abweichung erwartet wird und wir nichts auslösen. Bei false lesen Sie matches_ip als Information, nicht als Urteil. |
| reasons | string[] | Maschinenlesbare Codes dafür, welche Signale ausgelöst haben (z. B. "vpn_detected", "datacenter_asn", "antidetect_browser"). |
| evaluated_in_ms | integer | Serverseitige Verarbeitungszeit für diese Anfrage in Millisekunden. |
| device_ |
string | Ob die Browserprüfung verwertbar war: "ok" (das Geräte-Event wurde aufgelöst, device ist vorhanden), "unavailable" (eine Event-ID wurde gesendet, ließ sich aber nicht auflösen: fehlerhaft, abgelaufen, von anderswo oder die Prüfung war nicht erreichbar) oder "not_sent" (keine Event-ID, etwa bei einem Formular, aus dem das Feld sentinel_fp entfernt wurde). Vorhanden bei Live-Auswertungen, nicht bei Test-Tokens, in der Sandbox oder bei Beispielen. Ändert decision nie: Halten Sie sensible Aktionen wie Registrierungen, Auszahlungen und Passwort-Resets selbst an, wenn der Wert nicht "ok" ist. |
| evidence. |
integer / boolean | Wie frisch die Belege waren, nur mit dem, was bekannt ist: Sekunden seit der Netzwerkmessung (network_age_s) und seit dem Geräte-Event (device_age_s) sowie ob das Geräte-Event als Wiederholung eines früheren markiert wurde (device_replayed). Fehlt, wenn nichts davon bekannt ist. Nur Information; alte oder wiederholte Belege verdienen vor einer sensiblen Aktion eine neue Prüfung. |
{
"status": "success",
"isSuspicious": true,
"decision": "review",
"risk_score": 65,
"ip": "185.107.80.12",
"country": "EE",
"network": {
"vpn": true,
"proxy": false,
"datacenter": true,
"tor": false,
"anonymous": true,
"residential": false,
"service": "PROTON_VPN"
},
"reasons": ["vpn_detected", "datacenter_asn"],
"evaluated_in_ms": 126,
"details": {
"ip": "185.107.80.12",
"cc": "EE",
"vpn": true,
"proxied": false,
"anon": true,
"dch": true,
"service": "PROTON_VPN"
}
}{
"status": "success",
"isSuspicious": false,
"decision": "allow",
"risk_score": 0,
"ip": "82.131.45.9",
"country": "DE",
"network": {
"vpn": false,
"proxy": false,
"datacenter": false,
"tor": false,
"anonymous": false,
"residential": true,
"service": null
},
"reasons": [],
"evaluated_in_ms": 118
}Wie die Entscheidung zustande kommt
Die Engine gibt bei einem Proxy, Tor-Exit, erkannten Bot, Emulator oder einer Browsermanipulation block zurück. Ein VPN allein oder ein Browser in einer virtuellen Maschine ergibt review. Weitere Signale und Ihre Richtlinien können die endgültige Antwort beeinflussen. Ein Signal für einen Cloudserver oder ein anonymes Netzwerk allein führt nicht zu einer harten Sperre. Ihre Regeln und Ausnahmen können die Entscheidung überschreiben; engine_decision wird mitgeliefert, wenn eine Überschreibung die Entscheidung ändert, nicht bei jeder Antwort.
Eingeschränkte Antworten. Fehlende Netzwerkdaten führen zu degraded: true, ip: "unknown" und Netzwerkflags mit dem Wert false. Diese false-Werte bedeuten, dass die Daten nicht verfügbar sind, nicht dass die Verbindung unauffällig ist. Geräte- oder E-Mail-Signale, Regeln und Ausnahmen können weiterhin review oder block ergeben. Nur der reine, nicht verdächtige allow-Fallback wird aus den Auswertungszählern und Events ausgenommen; aussagekräftige Entscheidungen unterliegen der normalen Ereignis- und Verbrauchsverarbeitung. Authentifizierte Versuche verbrauchen weiterhin das stündliche Anfragekontingent.
Zusätzliche Flags. test: true bei jeder Antwort auf ein Test-Token oder einen Testschlüssel, sandbox: true, wenn der öffentliche sk_test_sandbox-Schlüssel verwendet wurde, sample: true bei /v1/evaluate/sample. /v1/lookup trägt zusätzlich ein additives network.cloud-Objekt, das den Anbieter nennt, wenn die Adresse in einem veröffentlichten Cloud-Bereich liegt.
Begründungscodes
Jeder Wert, den reasons[] tragen kann, und was er bedeutet. Neue Codes kommen nur hinzu.
anonymous_network— ein anonymisierendes Netz ohne benannten Betreiberantidetect_browser— ein Fake-Browser (Antidetect-Tooling oder eine manipulierte Umgebung)automation_detected— ein Script oder Headless-Browserdatacenter_asn— eine Rechenzentrums- oder Cloud-Adressedisposable_email— eine Wegwerf-E-Mail-Adresseemulator_detected— ein Emulatorhigh_activity_device— ein ungewöhnlich oft gesehenes Gerät (weiches Signal)ip_blocklisted— eine Adresse auf einer Blocklistemulti_account_device— ein Gerät, das bereits hinter mehreren Ihrer Konten stecktprivate_browsing— ein Inkognito- oder privates Fensterproxy_detected— ein Residential- oder Cloud-Proxytimezone_mismatch— die Zeitzone des Browsers widerspricht der Adresse (nur wenn geprüft)tor_exit_node— ein Tor-Exitvirtual_machine— ein Browser in einer virtuellen Maschine (allein: review)vpn_detected— ein VPN-Dienst (network.service nennt ihn, wenn bekannt)
Test-Token
Senden Sie eines davon als token mit Ihrem Live-Schlüssel, Ihrem Testschlüssel oder sk_test_sandbox. Mit Ihren eigenen Schlüsseln befolgen sie Ihre Regeln und Ausnahme-Pins (test_vpn ist damit ein Einzeiler, um eine Regel zu prüfen); sie werden nie berechnet. Aus der Konsole gemachte Aufrufe werden als Testzeilen gespeichert, damit unter Ereignisse etwas zu sehen ist.
| Token | Vorgetäuschter Besucher | Signale | Entscheidung |
|---|---|---|---|
test_clean | 203.0.113.42 · US | nichts | allow |
test_vpn | 198.51.100.18 · NL | VPN, Rechenzentrum (PROTON_VPN) | review |
test_proxy | 203.0.113.9 · DE | Proxy, Rechenzentrum (BRIGHT_DATA) | block |
test_datacenter | 203.0.113.7 · DE | Rechenzentrum (AWS) | allow |
test_tor | 203.0.113.99 · null | Tor (TOR) | block |
IP-Lookup
Ein Urteil für eine bloße IP-Adresse — kein Browser-Token nötig. Gleicher Schlüssel und gleiches Stundenlimit wie /v1/evaluate; Lookups haben ein eigenes Monatskontingent, das 10-Fache der Besucherprüfungen des Tarifs (100.000 im Monat im kostenlosen Tarif).
GET /v1/lookup/{ip} — für serverseitiges Screening, wo kein Client läuft: Allowlist-Prüfungen, Batch-Scoring, Anreichern Ihrer eigenen Logs. Nur Netzwerksignale (es gibt kein Gerät zum Fingerprinten), nutzen Sie also /v1/evaluate, sobald ein Browser beteiligt ist. Bei einer bloßen IP greifen die Tor-Exit- und Cloud-Bereichsprüfungen; VPN- und Proxy-Tunnel — und der Dienstname in network.service — werden bei einem Live-Besuch über /v1/evaluate erkannt, sodass signals.vpn und signals.proxied hier false zurückkommen.
curl https://maskbreak.com/v1/lookup/185.220.101.34 \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"
{
"ip": "185.220.101.34",
"known": true,
"verdict": "block", // allow | review | block
"risk_score": 90,
"signals": { "vpn": false, "proxied": false, "tor": true, "dch": false, "anon": true },
"network": { "asn": 60729, "org": "Tor exit node", "country": null, "city": null,
"registration_country": "DE", "source": "asn_table" },
"latency_ms": 12
}
known: false bedeutet, dass keine Intelligence-Quelle eine Meinung hatte — das Urteil ist dann allow mit risk_score: 0, was „nichts gefunden“ heißt, keine Garantie für eine unbedenkliche Adresse. Ausnahme-Pins, die Sie im Dashboard konfigurieren, gelten auch hier (verdict_source: "exception" mit additiv erhaltenem engine_verdict). Die Signalschreibweisen (proxied, dch) sind eingefroren — sicher zu parsen.
Ratenlimits
Jeder Tarif hat ein Monatskontingent an Besucherprüfungen (Aufrufe von /v1/evaluate, einschließlich evaluate() der SDKs) und ein Stundenlimit zum Schutz des Dienstes; parallel dazu läuft eine Grenze pro Adresse. Welches Limit Sie zuerst erreichen, liefert 429.
| Geltungsbereich | Limit |
|---|---|
| Kostenloser Tarif | 10.000 Besucherprüfungen / Monat · bis zu 1.000 / Stunde |
| Starter | 50.000 / Monat · bis zu 5.000 / Stunde |
| Growth | 250.000 / Monat · bis zu 20.000 / Stunde |
| Scale | 1.000.000 / Monat · bis zu 50.000 / Stunde |
| Enterprise | Vertraglich vereinbart |
| Öffentliches Interesse | Kein Monatskontingent, kein Stundenlimit |
| Pro Quell-IP | 200.000 Anfragen / Stunde (freigegebene Schlüssel im öffentlichen Interesse sind davon ausgenommen) |
Monatskontingente zählen pro Kalendermonat in UTC und werden am 1. um 00:00 UTC zurückgesetzt; ungenutzte Prüfungen verfallen. /v1/lookup und das MCP-Tool lookup_ip haben ein eigenes Monatskontingent, das 10-Fache der Besucherprüfungen des Tarifs (100.000 im kostenlosen Tarif), und teilen das Stundenlimit des Tarifs. Die festen test_*-Tokens und der öffentliche Sandbox-Schlüssel werden nie gezählt; Aufrufe mit Ihrem Testschlüssel und Prüfungen aus der Konsole durchlaufen die volle Erkennung und zählen zum Monatskontingent. Es gibt keine Überschreitungsgebühren: Ist das Monatskontingent aufgebraucht, liefern Aufrufe 429 mit "code": "monthly_quota_exceeded" und einem Retry-After, der bis zum 1. herunterzählt — bis der Monat zurückgesetzt wird oder Sie upgraden. Konten, die vor dem 1. Oktober 2026 erstellt wurden, behalten bis zum 31. Oktober 2026 1.000 Prüfungen pro Stunde ohne Monatslimit. Die Grenze pro IP ist ein Schutz gegen ausufernden Missbrauch von einer einzelnen Maschine. Retry-After ist in jeder 429-Antwort enthalten. Tarife und Preise
Wenn das Monatskontingent aufgebraucht ist
HTTP/1.1 429 Too Many Requests Retry-After: 172800 // Sekunden bis zum 1. um 00:00 UTC { "error": "…", // für Menschen lesbar; verzweigen Sie anhand von code "code": "monthly_quota_exceeded" }
Mit Schlüssel aufgerufene /v1/evaluate- und /v1/lookup-Antworten tragen außerdem X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset (Unix-Sekunden), damit Sie vor dem 429 bremsen können — sowie einen X-Request-Id-Header, den Sie dem Support nennen können, wenn etwas seltsam aussieht.
Schlüssel im Programm für das öffentliche Interesse (Krankenhäuser, öffentliche Gesundheit, Behörden, Wahlen, Rettungsdienste, Universitäten, eingetragene gemeinnützige Organisationen) haben kein Stundenlimit: Die drei X-RateLimit-*-Header werden weggelassen statt mit einem Platzhalter gefüllt, und /v1/usage meldet hourly_limit: null mit uncapped: true.
Zuverlässigkeit & Fail-open-Verhalten
Maskbreak liefert die besten verfügbaren Daten, wenn eine Analyseschicht nicht verfügbar ist. degraded: true meldet ausdrücklich fehlende Netzwerkdaten; ein Ausfall des Geräteanalyse-Anbieters allein setzt dieses Flag nicht. Ein fehlendes device-Objekt ist kein Beweis dafür, dass die Browserprüfungen bestanden wurden. Bei einem Netzwerkausfall oder fehlendem Token wird nur der reine, nicht verdächtige allow-Fallback von den Auswertungszählern und Events ausgenommen. Unabhängige Signale oder Richtlinienüberschreibungen können weiterhin ein gespeichertes Ergebnis review oder block liefern; bei persönlichen Testschlüsseln gilt weiterhin die Verarbeitung als Testereignis. Versuche verbrauchen weiterhin das stündliche Anfragekontingent. Ein vom Anbieter abgelehntes Token oder ein Token, das kein String ist, liefert stattdessen 400 Invalid token.. Legen Sie für fehlende Daten einen expliziten Fallback pro Endpunkt fest; setzen Sie nicht verfügbare Prüfungen nicht mit einem sicheren Besucher gleich.
Fehlercodes
Alle Fehlerantworten der API enthalten ein error-Stringfeld. Eine Ausnahme, unter 403 vermerkt: Eine Sperre am Edge liefert Klartext, kein JSON.
-
400
Bad Request — Ungültiges
token(vom Anbieter abgelehnt oder kein String). Ein fehlendes oder leeres Token ist kein Fehler: Es wird fail-open mitdegraded: truebeantwortet. - 401 Unauthorized — Ungültiger oder fehlender API-Schlüssel.
-
403
Forbidden — Konto gesperrt (kontaktieren Sie support@maskbreak.com), oder die IP des Aufrufers steht nicht auf der IP-Allowlist Ihres Schlüssels (Einstellungen → API-Schlüssel → Erlaubte Serveradressen; das Feld
hintnennt die aufrufende Adresse). Von derselben Adresse nicht wiederholbar.
Dritte Ursache, und die verwirrende: Ein bloßeserror code: 1010alstext/plainohne JSON-Body kommt nicht von uns — das ist unser Edge, der den Standard-User-Agent Ihres HTTP-Clients ablehnt.Python-urllibist der häufige Fall. Setzen Sie irgendeinen echtenUser-Agent-Header, und es verschwindet; jedes offizielle SDK tut das bereits. -
429
Rate Limited — Das Stundenlimit des Tarifs (1.000 pro Stunde im kostenlosen Tarif) oder sein Monatskontingent ist aufgebraucht, oder das separate Schutzlimit pro Adresse wurde erreicht. Ein aufgebrauchter Monat trägt
"code": "monthly_quota_exceeded", und seinRetry-Afterzählt bis zum 1. um 00:00 UTC herunter. Der Testschlüssel hat ein eigenes Stundenlimit; seine Prüfungen zählen zum selben Monatskontingent. Genehmigte Konten im öffentlichen Interesse haben kein Limit pro Konto, sind aber nicht von sämtlichen Schutzmaßnahmen ausgenommen:/v1/usage, Beispielantworten, OAuth und der Schutz vor ungültigen Schlüsseln haben unabhängige Limits. Beachten SieRetry-After, wenn es zurückgegeben wird. - 500 Server Error — Unerwarteter interner Fehler. Verwenden Sie begrenzte Wiederholungsversuche mit zunehmenden Wartezeiten innerhalb der Frist Ihrer Aktion. Ein erneuter Versuch kann eine bereits abgeschlossene Auswertung wiederholen; wiederholen Sie niemals blind eine Zahlung oder eine andere zustandsändernde Geschäftsoperation.
-
503
Service Unavailable — mit
codestorage_unavailable: Wir konnten Ihren Schlüssel oder Ihr Konto in diesem Moment nicht prüfen (unser Kontospeicher hat nicht geantwortet). Das ist kein ungültiger Schlüssel: Ein Schlüssel, der zu keinem Konto passt, ist immer 401. Es wurde nichts ausgewertet und kein Kontingent verbraucht. Wiederholen Sie nachRetry-After(30 Sekunden) und wenden Sie bis dahin Ihre Ausfallregel an; werten Sie die Antwort nie als Erlauben.
Verbrauch
Der Kontingentstand Ihres Schlüssels als Endpunkt — fragen Sie ihn ab, statt Ratenlimit-Header aus Ihrer letzten Antwort auszulesen.
GET /v1/usage — authentifizieren Sie sich mit dem API-Schlüssel selbst (Authorization: Bearer …). Funktioniert mit beiden Schlüsseltypen — sk_live_ und sk_test_ melden jeweils ihr eigenes Stundenkontingent, benannt in key_type — und der Aufruf ist kostenlos: Er verbraucht kein Kontingent, sodass ein Monitor, der ihn abfragt, Ihr Limit nicht anknabbern kann.
curl https://maskbreak.com/v1/usage \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"
{
"key_type": "live", // or "test"
"plan": "free", // free | starter | growth | scale | custom | public_interest
"monthly_limit": 10000,
"month_used": 2481,
"monthly_lookup_limit": 100000,
"month_lookups_used": 5210,
"month_resets_at": "2026-08-01T00:00:00.000Z", // 00:00 UTC on the 1st of next month
"grandfathered_until": null, // set while an account created before 1 Oct 2026 keeps its previous allowance
"hourly_limit": 1000,
"used_this_hour": 412,
"remaining": 588,
"resets_at": "2026-07-19T11:00:00.000Z", // end of the rolling 60 minutes that began with your first call; null if nothing used yet
"total_evaluations": 183204,
"limit_hits": 3
}
used_this_hour und remaining sind Richtwerte: Die Stundenzähler liegen im Speicher und pro Prozess, sie werden bei einem Deployment zurückgesetzt — behandeln Sie sie als Live-Anzeige, nicht als Prüfprotokoll (die Durchsetzung des eigentlichen Limits ist davon unberührt). total_evaluations (jeder gezählte Aufruf von /v1/evaluate, /v1/lookup und MCP lookup_ip; month_used und month_lookups_used teilen den laufenden Monat nach Art auf) und limit_hits sind dauerhafte Kontosummen. Seit dem 30. September 2026 zusätzlich (additiv): plan (free, starter, growth, scale, custom oder public_interest), monthly_limit und month_used (Besucherprüfungen in diesem Kalendermonat), monthly_lookup_limit und month_lookups_used (Aufrufe von /v1/lookup und MCP lookup_ip in diesem Monat), month_resets_at (1. des nächsten Monats, 00:00 UTC) und grandfathered_until (wann das bisherige Kontingent eines vor dem 1. Oktober 2026 erstellten Kontos endet, sonst null). Die bestehenden Felder bleiben unverändert.
Limits und Hinweise. Das Stundenfenster beginnt mit Ihrem ersten Aufruf; das Monatskontingent folgt dem Kalendermonat (UTC). Nutzungswarnungen werden dem Inhaber bei 80 % und beim Erreichen des Stundenlimits gesendet, für einen Live-Schlüssel höchstens einmal je Schwelle innerhalb von 24 Stunden, sowie bei etwa 80 % und 100 % des Monatskontingents. Der Testschlüssel löst keine Stundenwarnungen aus; seine Prüfungen zählen zum Monatskontingent und können daher die Monatswarnungen auslösen. /v1/usage verbraucht kein Auswertungskontingent, ist aber auf 120 Lesezugriffe pro Minute und Adresse begrenzt, auch für Konten im öffentlichen Interesse (429 mit Retry-After). Der Endpunkt setzt die IP-Allowlist des Schlüssels nicht durch; eine Kontosperrung führt weiterhin zu 403. Wiederholte ungültige Zugangsdaten können eine 15-minütige Adresssperre auslösen (429, Retry-After: 900).
Webhooks
Bedrohungsalarme an Slack, Discord oder Ihren eigenen Endpunkt — konfiguriert im Integration-Tab der Konsole unter Alarme.
Wann sie auslösen. Bei von der Engine erkannten Bedrohungen (VPN, Proxy, Tor, Rechenzentrum mit Manipulation, Bots, Antidetect-Browser, eine virtuelle Maschine (review)) und bei jeder Auswertung, die Ihre eigenen Regeln mit block entschieden haben. Slack- und Discord-URLs bekommen eine kanalfertige Nachricht; jeder andere HTTPS-Endpunkt erhält ein JSON-Event (event: "threat.detected" mit IP, Bedrohungstyp und den Signaldetails). Der Button Test senden im Dashboard liefert dieselbe Struktur mit event: "test" — wenn Ihr Empfänger strikt nach Event-Typ verzweigt, behandeln Sie beide, sonst sieht der Test-Button wie ein stiller Fehlschlag aus.
Echtheit prüfen. Jede Zustellung ist mit dem Signaturgeheimnis Ihres Kontos signiert (neben der Webhook-URL im Dashboard zu sehen). Berechnen Sie HMAC_SHA256(secret, timestamp + "." + rawBody) mit dem Header X-Maskbreak-Timestamp und vergleichen Sie es (in konstanter Zeit) mit dem Hex-Digest in X-Maskbreak-Signature (nach dem Präfix sha256=). Weisen Sie alles zurück, was unsigniert ist, nicht übereinstimmt oder älter als 5 Minuten ist.
Empfänger zum Kopieren. Die zwei klassischen Fehler sind, gegen den neu serialisierten geparsten Body statt gegen die rohen Bytes zu prüfen und mit == zu vergleichen. Diese Empfänger machen es richtig:
const crypto = require('crypto'); const express = require('express'); const app = express(); // Raw body required — verify the exact bytes Maskbreak signed, // never a re-serialized JSON.parse() of them. app.post('/webhooks/sentinel', express.raw({ type: 'application/json' }), (req, res) => { const secret = process.env.SENTINEL_WEBHOOK_SECRET; // shown next to the URL in the dashboard const ts = req.get('X-Maskbreak-Timestamp') || ''; const sig = req.get('X-Maskbreak-Signature') || ''; // Freshness: reject anything older than 5 minutes (replay defense) const age = Math.abs(Date.now() / 1000 - Number(ts)); if (!ts || !Number.isFinite(age) || age > 300) return res.status(400).end(); // "sha256=" + HMAC_SHA256(secret, timestamp + "." + rawBody), hex digest const expected = 'sha256=' + crypto.createHmac('sha256', secret) .update(ts + '.').update(req.body).digest('hex'); // Constant-time compare — never === const a = Buffer.from(sig), b = Buffer.from(expected); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) { return res.status(401).end(); } const event = JSON.parse(req.body); // { event: "threat.detected", event_id, ip, threat_type, details } // ... handle it (queue it, alert, block the IP upstream) res.status(200).end(); // answer 2xx fast — deliveries time out after 10 s });
import hashlib, hmac, os, time from flask import Flask, request, abort app = Flask(__name__) SECRET = os.environ["SENTINEL_WEBHOOK_SECRET"].encode() # shown next to the URL in the dashboard @app.post("/webhooks/sentinel") def sentinel_webhook(): ts = request.headers.get("X-Maskbreak-Timestamp", "") sig = request.headers.get("X-Maskbreak-Signature", "") # Freshness: reject anything older than 5 minutes (replay defense) try: fresh = abs(time.time() - int(ts)) <= 300 except ValueError: fresh = False if not fresh: abort(400) # "sha256=" + HMAC_SHA256(secret, timestamp + "." + raw_body), hex digest. # Sign request.get_data() — the exact raw bytes, not request.json. expected = "sha256=" + hmac.new( SECRET, ts.encode() + b"." + request.get_data(), hashlib.sha256 ).hexdigest() # Constant-time compare — never == if not hmac.compare_digest(sig, expected): abort(401) event = request.get_json(force=True) # {"event": "threat.detected", "event_id": ..., "ip": ..., "details": ...} # ... handle it (queue it, alert, block the IP upstream) return "", 200
Zustellung & Wiederholungen. Jedes Event wird bis zu 3-mal versucht: Eine fehlgeschlagene Zustellung wird nach etwa 1 Minute und dann nach etwa 8 Minuten wiederholt. Wiederholungen sind Best-Effort und laufen im Prozess — ein Deployment mitten in der Wiederholung verwirft die restlichen Versuche —, behandeln Sie also die Ereignisliste im Dashboard als Quelle der Wahrheit und den Webhook als Best-Effort-Push. Die 25 letzten Zustellungen (Event, Ergebnis, Fehler) stehen im Dashboard unter Entwickler-Tools → Webhook-Alarme, 30 Tage aufbewahrt; der Header-Chip zeigt den aktuellen Zustand, und schlagen 5 Zustellungen in Folge fehl, mailen wir Ihnen — ein toter Webhook darf nie als „keine Bedrohungen“ gelesen werden. Zustellungen laufen nach 10 s ab und folgen nie Weiterleitungen.
Dedupe über event_id. Jede Payload trägt eine event_id — 32 Hex-Zeichen, eindeutig pro Event, auch bei Test-Events vorhanden —, und jede Zustellung trägt sie zusätzlich im Header X-Maskbreak-Event-Id. Wiederholte Zustellungen verwenden dieselbe event_id, verwenden Sie sie also als Schlüssel für Ihre Verarbeitung, und jedes Event wird genau einmal verarbeitet, egal wie viele Versuche Sie erreichen.
Payload für generische Endpunkte. Was Ihr Empfänger tatsächlich bekommt (Slack-/Discord-URLs erhalten stattdessen eine formatierte Nachricht):
{
"event": "threat.detected",
"event_id": "9f2ce7a4c1d84b7fa3d2c05e8b6f4a19", // unique per event; same on retries
"timestamp": "2026-07-19T09:14:03.512Z",
"ip": "185.220.101.34",
"threat_type": "Tor", // or "Custom rule (…)" / "Customer exception (…)"
"details": {
"ip": "185.220.101.34", "cc": "DE",
"vpn": false, "proxied": false, "tor": true, "dch": false,
"bot": false, "tampering": false, "antidetect": false,
"virtual_machine": false, // since 29 Sep 2026; tampering = tampered browser or emulator
"decision": "block",
"rule_matched": null, // signals, when your rule caused the block
"exception_matched": null // pins, when your exception caused it
}
}
MCP für KI-Agenten
Ein gehosteter Model-Context-Protocol-Server — richten Sie einen KI-Agenten darauf, und er kann IPs mit echten Maskbreak-Urteilen prüfen.
Endpunkt: POST https://maskbreak.com/mcp (streamable HTTP, zustandslos). Tools: lookup_ip — dasselbe Urteil wie GET /v1/lookup — und service_status. Authentifizieren Sie sich entweder mit einem klassischen API-Schlüssel Authorization: Bearer sk_live_… oder mit einem OAuth-access_token aus POST /oauth/token. Anonyme Nutzung teilt sich die engen Pro-IP-Limits des kostenlosen Tools; authentifizierte Aufrufe teilen das Stundenlimit Ihres Tarifs und zählen zum selben Monatskontingent wie /v1/lookup (Ihre IP-Allowlist gilt auch hier).
{
"mcpServers": {
"sentinel": {
"type": "http",
"url": "https://maskbreak.com/mcp",
"headers": { "Authorization": "Bearer sk_live_YOUR_API_KEY" }
}
}
}
API-Stabilität & Versionierung
Worauf Sie bauen können, ohne sich zu sorgen, dass sich der Boden bewegt.
Versionierung. Die API ist im Pfad versioniert (/v1/). Innerhalb einer Hauptversion machen wir nur additive Änderungen: neue Antwortfelder, neue optionale Anfrageparameter, neue Signalgründe. Ihre Integration muss unbekannte Felder in Antworten tolerieren — das ist die einzige Anforderung an Vorwärtskompatibilität, die wir an Sie stellen.
Breaking Changes. Antwortfelder umbenennen oder entfernen, Typen oder Bedeutungen ändern oder einen Endpunkt zurückziehen passiert nur in einer neuen Hauptversion (/v2/). Wenn dieser Tag kommt, funktioniert /v1/ noch mindestens 12 Monate nach der Ankündigung weiter.
Ankündigung von Abkündigungen. Jede Abkündigung wird mindestens 90 Tage im Voraus über das Changelog und per E-Mail an betroffene API-Schlüssel angekündigt, mit dokumentiertem Migrationspfad. Enterprise-Verträge können längere Supportfenster festschreiben — support@maskbreak.com.
Häufige Fragen
monthly_quota_exceeded; Retry-After zählt bis zum 1. um 00:00 UTC herunter). Es gibt keine Überschreitungsgebühren: Mit einem Upgrade in der Konsole erhöhen Sie das Kontingent, von Starter für 29 € im Monat bis Enterprise ab 990 € im Monat. Organisationen im öffentlichen Interesse (Krankenhäuser, öffentliche Gesundheit, Behörden, Wahlen, Rettungsdienste, Universitäten, eingetragene gemeinnützige Organisationen) können auf der Seite des Programms für das öffentliche Interesse beantragen, dass das Limit entfernt wird.