v1 Lädt…

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 19. Oktober 2026 ·jede API-Änderung landet im Changelog · Stabilitätsrichtlinie: nur additive Änderungen

Playbooks je Angriff: Kontoübernahme, Card Testing, Bonusmissbrauch, und mehr →

★

Schnellstart

Drei Schritte zu Ihrer ersten vollständigen Prüfung: Fügen Sie das Skript auf der Seite mit Ihrem Formular ein, senden Sie die Prüfung von Ihrem Server, und senden Sie Ihr Formular einmal ab. Erstellen Sie einen kostenlosen Schlüssel unter /signup — 10.000 Besucherprüfungen im Monat im kostenlosen Tarif, keine Karte.

Node.js-SDK npm install @sentinelsup/sdk npm ↗ · GitHub ↗
Python-SDK pip install sentinelsup PyPI ↗ · GitHub ↗
OpenAPI 3.1 https://maskbreak.com/openapi.json Spec ↗ ·
Mit KI einrichten Claude Code · Cursor · Copilot · Windsurf

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. Start in watch mode (MASKBREAK_MODE). Read the API key from the server-only MASKBREAK_API_KEY environment variable; I will configure the secret separately. Never put it in client-side code. Then show me how to test it.
Ihr Maskbreak-API-Endpunkt
Lädt…
Ihr Backend ruft diese URL auf. Ihr API-Schlüssel authentifiziert die Anfrage.
Im Beobachtungsmodus starten

Das Beispiel startet im Beobachtungsmodus: Es sendet jede Übermittlung an Maskbreak, auch eine mit fehlendem Feld (das Dashboard zeigt dann, welche Hälfte nicht angekommen ist), protokolliert die Entscheidung und lässt jeden Besucher weiter. Sobald die Ereignisse im Dashboard stimmig aussehen, setzen Sie MASKBREAK_MODE=enforce und deployen neu: Derselbe Handler stoppt dann block und hält review, fehlende Nachweise und Fehler zurück. Sind die Prüfungen des Monats aufgebraucht (HTTP 429 mit dem Code monthly_quota_exceeded), lässt er die Aktion bis zum 1. ungeprüft durch und protokolliert das, damit ein aufgebrauchtes Kontingent Ihr Formular nie lahmlegt. Der Integrationsleitfaden enthält denselben Handler und ein Beispiel für den Next.js App Router.

Integrationsmuster — behalten Sie Ihren bestehenden Login-Handler bei
// STEP 1 — HTML. The script on the page with your form, the class on the form itself.
// Your site sends a Content-Security-Policy (helmet's default does)? Add Maskbreak's
// hosts first, or the browser blocks the script: maskbreak.com/integrate.md#content-security-policy
<script async 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. It never holds the form on Maskbreak:
// a blocked or missing script only means the check gets less evidence.
document.getElementById('login-form').addEventListener('submit', async (event) => {
  event.preventDefault();
  const evidence = window.Sentinel ? await window.Sentinel.collect() : {};
  const response = await fetch('/your-login-endpoint', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    // evidence = { token, fingerprintEventId, tz }, already the API's names
    body: JSON.stringify({ email: event.target.email.value, ...evidence })
  });
  // Handle response.status and the next step in your existing login UI.
});

// STEP 3 — Node 18+ / Express backend.
// express, app and existingLoginHandler belong to your application.
// This check is NOT authentication and never creates a login session.
// MASKBREAK_MODE unset or 'watch' (start here): send every submission, also one
// with a missing field (the dashboard then says which half did not arrive),
// log the answer and always continue. 'enforce': stop block; hold review,
// missing evidence and errors (a used-up month continues, logged).
const MASKBREAK_MODE = process.env.MASKBREAK_MODE || 'watch';
app.use(express.json({ limit: '16kb' }));
app.use(express.urlencoded({ extended: false, limit: '16kb' }));
app.post('/your-login-endpoint', async (req, res, next) => {
  const body = req.body || {};
  // Form fields -> API names: monocle -> token, sentinel_fp -> fingerprintEventId,
  // sentinel_tz -> tz. Sentinel.collect() already returns the API names.
  const token = body.token || body.monocle;
  const fingerprintEventId = body.fingerprintEventId || body.sentinel_fp;
  const tz = body.tz || body.sentinel_tz;
  const missing = typeof token !== 'string' || !token ||
    typeof fingerprintEventId !== 'string' || !fingerprintEventId;
  let result = null, hold = null, monthUsedUp = false, status = null;
  if (!missing || MASKBREAK_MODE !== 'enforce') {
    try {
      const response = await fetch('https://maskbreak.com/v1/evaluate', {
        method: 'POST',
        signal: AbortSignal.timeout(5000), // example budget; measure your own
        headers: {
          'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ token, fingerprintEventId, tz, email: body.email })
      });
      status = response.status; // 401: MASKBREAK_API_KEY is empty or wrong
      if (!response.ok) {
        const refusal = await response.json().catch(() => null);
        // The month's checks are used up: let the action through unchecked
        // until the 1st (00:00 UTC) and log it, as watch mode does.
        if (response.status === 429 && refusal?.code === 'monthly_quota_exceeded') monthUsedUp = true;
        else throw new Error('Visitor check unavailable');
      } else {
        result = await response.json();
        // Production gate: never authorize from a fixture or personal test key.
        if (!result || result.test || result.sandbox || result.sample ||
            !['allow', 'review', 'block'].includes(result.decision)) {
          throw new Error('No production verdict');
        }
        if (result.decision === 'block') {
          hold = [403, { error: 'Action refused by policy' }];
        } else if (result.decision === 'review' || result.degraded || !result.device) {
          hold = [409, { next: 'step_up' }]; // implement a real step-up flow
        }
      }
    } catch {
      // Example temporary hold. Choose a fallback for each business action.
      hold = [503, { error: 'Check unavailable; try again shortly' }];
    }
  }
  if (missing) hold = [409, { next: 'step_up' }]; // enforce never trusts half a check
  // One line per check. Never log the key, browser tokens or form data.
  console.log('[maskbreak]', MASKBREAK_MODE, 'HTTP ' + (status ?? 'none'),
    result ? result.decision : monthUsedUp ? 'monthly_quota_exceeded' : 'no answer',
    hold ? 'enforce would return ' + hold[0] : 'enforce would continue');
  if (MASKBREAK_MODE === 'enforce' && hold) {
    return res.status(hold[0]).json(hold[1]); // never mint a session here
  }
  req.fraudCheck = result;
  return next(); // normal credential + authorization checks still run
}, 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.

Anfrage
GET https://maskbreak.com/v1/evaluate/sample
   ?scenario=clean
Keine Authentifizierung nötig · 30 Anfragen/Min · liefert Beispieldaten
Antwort
wartet auf Ausführung…
{
  "auf ▶ Anfrage ausführen klicken": "um Live-JSON zu sehen"
}

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.

Eine echte IP nachschlagen
echtes Pipeline-Urteil · kein Schlüssel · ratenlimitiert — ein paar Versuche pro Minute
Anfrage
POST https://maskbreak.com/api/lookup
{ "ip": "185.220.101.34" }
Echtes Urteil aus der Live-Pipeline (ein Tor-Exit ist vorausgefüllt). Streng ratenlimitiert — ein paar Versuche pro Minute; für Volumen nutzen Sie GET /v1/lookup mit einem Schlüssel.
Antwort
wartet auf Ausführung…
{
  "auf ▶ Lookup ausführen klicken": "für ein Live-Urteil zu dieser IP"
}

So funktioniert es

Maskbreak nutzt einen dreistufigen Ablauf Client → Ihr Backend → Maskbreak-API, damit Ihr geheimer Schlüssel nie im Browser landet.

1
Erfassung im Browser

Das Maskbreak-SDK läuft im Browser Ihres Besuchers auf der Seite mit Ihrem Formular und fügt ihm zwei versteckte Felder hinzu: das verschlüsselte Netzwerk-Token (monocle) und die Browserprüfung (sentinel_fp).

2
Token weitergeleitet

Ihr Formular sendet beides zusammen mit den übrigen Formulardaten an Ihren eigenen Backend-Server.

3
API-Auswertung

Ihr Backend ruft POST /v1/evaluate mit Ihrem geheimen Schlüssel auf und sendet beides als token und fingerprintEventId. Maskbreak antwortet mit allow, review oder block und den Gründen.

Authentifizierung

Alle Anfragen an /v1/evaluate müssen Ihren geheimen API-Schlüssel als Bearer-Token enthalten. Ihren Schlüssel finden Sie im Dashboard.

Authorization: Bearer sk_live_YOUR_SECRET_KEY
Halten Sie Ihren Schlüssel geheim
Schreiben Sie 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.

OAuth-Token-Endpunkt
POST /oauth/token
Tauscht E-Mail + API-Schlüssel gegen ein Bearer-Token. Discovery-Metadaten unter /.well-known/oauth-authorization-server und /.well-known/oauth-protected-resource.
cURL
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.

1

SDK einbinden

Binden Sie das Maskbreak-SDK auf der Seite mit Ihrem Formular ein. Ein Script lädt beide Erkennungsschichten — Netzwerkintelligenz (VPN, Proxy, Rechenzentrum) und Geräteintelligenz (Antidetect-Browser, Bots, Manipulation) — und fügt beide in jedes Formular mit class="monocle-enriched" ein (am Formular selbst, nie an einem Eingabefeld).

HTML — die Seite mit Ihrem Formular
<!-- Maskbreak SDK — network + device, on the page with your form -->
<script async src="loading..."></script>

<!-- class="monocle-enriched" on the form itself, never on an input -->
<form class="monocle-enriched" id="my-form">
  <!-- The SDK fills these in by itself: -->
  <input type="hidden" name="monocle"    value="eyJ..." /> // network
  <input type="hidden" name="sentinel_fp" value="a1b2..." /> // device
  <input type="hidden" name="sentinel_tz" value="Europe/Tallinn" /> // time zone
</form>

Ihre Website sendet eine Content-Security-Policy? Die Standardrichtlinie von helmet tut das und blockiert das Skript, bis Sie Ihre Direktiven um die eigenen Hosts des SDK ergänzen: 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. Der Integrationsleitfaden zeigt die Konfiguration für helmet.

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.

2

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, tz }, bereits unter den Namen der API. Leiten Sie das an Ihr Backend weiter. Prüfen Sie vorher, ob window.Sentinel existiert, und halten Sie das Formular nie an, wenn das Skript fehlt oder blockiert ist. (Ein gewöhnlich abgesendetes Formular enthält stattdessen die versteckten Felder monocle, sentinel_fp und sentinel_tz: Ihr Server sendet sie als token, fingerprintEventId und tz.) 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.

JavaScript (Frontend)
document.getElementById('my-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  // Both layers (network token + device event id) and the time zone.
  // No script (blocked or still loading)? Send the form anyway.
  const evidence = window.Sentinel ? await window.Sentinel.collect() : {};

  // Forward them 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,
      ...evidence  // ← token, fingerprintEventId, tz: the API's own names
    })
  });
});
3

Den Evaluate-Endpunkt aufrufen

POST 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

token string für vollständige Netzwerkprüfungen
Das verschlüsselte Netzwerk-Token aus Sentinel.collect() oder input[name="monocle"] (auch der Feldname monocle wird akzeptiert; werden beide gesendet, gilt token). 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.
fingerprintEventId string
Die Event-ID der Geräteintelligenz — das SDK erfasst sie und fügt sie als 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.
accountId string
Optional: Ihre eigene Konto-/Nutzer-ID für diese Sitzung. Zusammen mit 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. Maskbreak speichert einen geschlüsselten Hash und eine verschlüsselte Kopie, die nur Ihre Konsole anzeigt; beides wird 90 Tage nach der letzten Prüfung des Kontos gelöscht. Seit dem 18. Oktober 2026 füllt sie auch das Objekt account.
email string
Optional: die E-Mail-Adresse, mit der sich der Besucher registriert. Fügt der Antwort 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.
tz string
Optional: die IANA-Zeitzone des Besuchers, z. B. 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.

// Your backend. Form fields -> API names: monocle -> token,
// sentinel_fp -> fingerprintEventId, sentinel_tz -> tz (collect() already uses them).
// Watch mode (start here) sends every submission, a missing field included.
const b = req.body || {};
let data = null;
try {
  const response = await fetch('https://maskbreak.com/v1/evaluate', {
    method: 'POST',
    signal: AbortSignal.timeout(5000),
    headers: {
      'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      token: b.token || b.monocle,
      fingerprintEventId: b.fingerprintEventId || b.sentinel_fp,
      tz: b.tz || b.sentinel_tz
    })
  });
  data = response.ok ? await response.json() : null;
} catch {
  data = null; // unavailable: watch mode continues
}

// data.decision → 'allow' | 'review' | 'block' (covers VPN, proxy,
// datacenter, Tor, antidetect, automation) — route on this
// data.reasons  → machine-readable why
console.log('[maskbreak]', data ? data.decision : 'no answer');

if (process.env.MASKBREAK_MODE === 'enforce' && data && data.decision === 'block') {
  return res.status(403).json({ error: 'Suspicious connection.' });
}
# Your backend (Flask shown): JSON from Sentinel.collect(), or a posted form.
# Form fields -> API names; watch mode (start here) sends every submission.
import os, requests

body = request.get_json(silent=True) or request.form
try:
    data = requests.post(
        "https://maskbreak.com/v1/evaluate",
        headers={"Authorization": "Bearer " + os.environ["MASKBREAK_API_KEY"]},
        json={
            "token": body.get("token") or body.get("monocle"),
            "fingerprintEventId": body.get("fingerprintEventId") or body.get("sentinel_fp"),
            "tz": body.get("tz") or body.get("sentinel_tz"),
        },
        timeout=5,
    ).json()
except (requests.RequestException, ValueError):
    data = None  # unavailable: watch mode continues

# data["decision"] → 'allow' | 'review' | 'block' — route on this
# data["reasons"]  → machine-readable why
print("[maskbreak]", (data or {}).get("decision", "no answer"))

if os.environ.get("MASKBREAK_MODE") == "enforce" and (data or {}).get("decision") == "block":
    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"}'
// Your backend: JSON from Sentinel.collect(), or a posted form.
// Form fields -> API names; watch mode (start here) sends every submission.
$in = json_decode(file_get_contents('php://input'), true) ?: $_POST;
$ch = curl_init('https://maskbreak.com/v1/evaluate');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 5,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('MASKBREAK_API_KEY'), 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode([
        'token' => $in['token'] ?? $in['monocle'] ?? null,
        'fingerprintEventId' => $in['fingerprintEventId'] ?? $in['sentinel_fp'] ?? null,
        'tz' => $in['tz'] ?? $in['sentinel_tz'] ?? null,
    ]),
]);
$data = json_decode((string) curl_exec($ch), true); // null when unavailable: watch mode continues

// $data['decision'] → 'allow' | 'review' | 'block' — route on this
// $data['reasons']  → machine-readable why
error_log('[maskbreak] ' . ($data['decision'] ?? 'no answer'));

if (getenv('MASKBREAK_MODE') === 'enforce' && ($data['decision'] ?? null) === 'block') {
    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. Ihre Website muss zuerst bestätigt sein: Die erste vollständige Prüfung von ihr bestätigt sie automatisch. Erfassen Sie dort das normale SDK-Ereignis und fordern Sie über Ihr Backend eine Challenge an. Erforderlich ist ein vom Anbieter verifiziertes Ereignis der bestätigten 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, den WebGL-Bericht mit dem angegebenen System sowie zwei neue Berechnungsergebnisse und bildet im Browser Prüfsummen von drei kleinen Canvas-Bildern (nur 8-stellige Kennungen verlassen ihn). 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=6 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. Drei Befunde heben ein allow auf review an, nie auf block: ein WebGL-Bericht, der sich selbst widerspricht (gpu_inconsistent); ein Konto, das auf demselben Gerätetyp zwei Canvas-Fingerprints aus zwei Browsern zeigt (antidetect_randomization, braucht eine accountId); und fünf oder mehr neue Canvas-Fingerprints auf einem Gerätetyp innerhalb eines Tages, jeder aus einem eigenen Browser (antidetect_profile_burst). Für die beiden letzten werden schlüsselbasierte Hashwerte der Canvas-Werte, der Gerätekennung des Browsers und, falls Sie eine senden, der Konto-ID 30 Tage lang nur für Ihr Konto gespeichert. 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.vpn boolean Kommerzielles VPN erkannt (NordVPN, Proton, ExpressVPN usw.). Gespiegelt unter details.vpn.
network.proxy boolean Residential- oder SOCKS/HTTP-Proxy erkannt. Auch true bei Apples iCloud Private Relay, das network.privacy_relay benennt. Gespiegelt unter details.proxied.
network.privacy_relay boolean true, wenn der Proxy Apples iCloud Private Relay ist, wie Maskbreaks Netzwerkintelligenz ihn erkennt (network.proxy ist ebenfalls true). Allein ergibt er review, nie block, mit dem Code privacy_relay statt proxy_detected. Eine Proxy-Regel auf Erlauben lässt ihn ebenfalls zu; auf Prüfen oder Blockieren gilt sie nicht für das Relay, das bei review bleibt. In jeder Auswertung seit dem 10. Oktober 2026.
network.datacenter boolean Traffic aus einem bekannten Rechenzentrums-/Cloud-Anbieter-ASN. Gespiegelt unter details.dch.
network.tor boolean Tor-Exit-Node erkannt. Gespiegelt unter details.tor.
network.anonymous boolean Irgendeine Anonymisierungsschicht erkannt. Gespiegelt unter details.anon.
network.residential boolean true, wenn weder Rechenzentrum noch Proxy ausgelöst haben (sieht nach einer echten Heim-IP aus).
network.service string Identifizierter Anbieter, wenn bekannt (z. B. "PROTON_VPN", "BRIGHT_DATA"). Gespiegelt unter details.service.
network.complete / network.sdk_blocked 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.antidetect boolean Antidetect-/Fingerprint-Spoofing-Browser (Multilogin, Kameleo, GoLogin usw.). Nur vorhanden, wenn fingerprintEventId gesendet wurde.
device.automation boolean Automatisierter Browser erkannt (Puppeteer, Playwright, Selenium).
device.emulator boolean Mobil-Emulator.
device.virtual_machine boolean Der Browser läuft in einer virtuellen Maschine. Allein ergibt das review (seit 29. September 2026).
device.incognito boolean Privater / Inkognito-Modus erkannt.
device.privacy_mode boolean Erweiterte Datenschutzeinstellungen aktiv (z. B. Brave Shields, Firefox resist-fingerprinting).
device.ip_blocklisted boolean Die Besucher-IP steht auf E-Mail-Spam- oder Angriffsquellen-Blocklisten.
device.high_activity 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.visitor_id 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.tampering_score 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.complete / device.checked_signals 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.times_seen 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.first_seen 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.returning boolean True, wenn times_seen > 1 — dieses Gerät wurde schon einmal ausgewertet.
device.linked_accounts 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. Die Verknüpfung wird als geschlüsselte Hashes gespeichert und nie mit einem anderen Kunden geteilt.
device.multi_account boolean True, wenn linked_accounts > 1 — das zentrale Multi-Accounting-Signal (Bonusmissbrauch, Testphasen-Missbrauch, doppelte Registrierungen). Fügt den Grund multi_account_device hinzu.
account.device_accounts_24h integer Ihre Konten, die in den letzten 24 Stunden auf diesem Gerät geprüft wurden, dieses eingeschlossen; null ohne Gerätedaten. Ab drei kommt device_many_accounts hinzu und aus Erlauben wird Prüfen. Das Objekt account ist vorhanden, wenn Sie accountId mit einem Live-Schlüssel senden (seit dem 18. Oktober 2026).
account.devices_24h integer Geräte, auf denen dieses Konto in den letzten 24 Stunden geprüft wurde. Ab drei kommt account_many_devices hinzu.
account.ip_accounts_24h integer Ihre Konten, die in den letzten 24 Stunden von dieser Adresse aus geprüft wurden, dieses eingeschlossen. Null, wenn keine Adresse gemessen wurde oder sie ein geteilter Ausgang ist (VPN, Proxy, Rechenzentrum, Tor-Exit, Apples Relay oder die geteilte Adresse eines Mobilfunkanbieters); solche Adressen werden nie verknüpft. Ab drei kommt ip_many_accounts hinzu.
account.new_device boolean True, wenn dieses Konto schon auf einem anderen Gerät geprüft wurde und noch nie auf diesem; fügt account_new_device hinzu. Null ohne Gerätedaten.
account.connected_accounts integer Ihre anderen Konten, die innerhalb von 90 Tagen ein Gerät oder eine Adresse mit diesem teilen; die Konsole listet sie auf. Nichts wird kundenübergreifend verknüpft.
email.disposable 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.reported string Nur vorhanden, wenn die Anfrage eine erkannte tz enthielt. Die vom Browser gemeldete IANA-Zone, zurückgespiegelt.
timezone.country string ISO-3166-1-alpha-2-Land, zu dem diese Zone gehört.
timezone.matches_ip 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.checked 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_evidence 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.network_age_s / evidence.device_age_s / evidence.device_replayed 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.
200 OK — Verdächtige Verbindung
{
  "status": "success",
  "isSuspicious": true,
  "decision": "review",
  "risk_score": 65,
  "ip": "185.107.80.12",
  "country": "EE",
  "network": {
    "vpn": true,
    "proxy": false,
    "privacy_relay": 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"
  }
}
200 OK — Saubere Verbindung
{
  "status": "success",
  "isSuspicious": false,
  "decision": "allow",
  "risk_score": 0,
  "ip": "82.131.45.9",
  "country": "DE",
  "network": {
    "vpn": false,
    "proxy": false,
    "privacy_relay": 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, ein Browser in einer virtuellen Maschine oder Apples iCloud Private Relay allein (der eine Proxy, der geprüft statt blockiert wird) 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.

  • account_many_devices — eines Ihrer Konten wurde innerhalb von 24 Stunden auf drei oder mehr Geräten geprüft (wenn Sie accountId senden; nur zur Information)
  • account_new_device — ein bereits geprüftes Konto auf einem Gerät, das es noch nie benutzt hat (wenn Sie accountId senden; nur zur Information)
  • anonymous_network — ein anonymisierendes Netz ohne benannten Betreiber
  • antidetect_browser — ein Fake-Browser (Antidetect-Tooling oder eine manipulierte Umgebung)
  • antidetect_profile_burst — fünf oder mehr neue Browser auf einem Gerätetyp innerhalb eines Tages, jeder mit eigenem Canvas-Fingerprint: die Fake-Browser-Profile eines Geräts (Browser-Konsistenz-Beta; nur review)
  • antidetect_randomization — ein Konto zeigt auf demselben Gerätetyp zwei Canvas-Fingerprints aus zwei Browsern: ein randomisierender Fake-Browser (Browser-Konsistenz-Beta mit Konto-ID; nur review)
  • automation_detected — ein Script oder Headless-Browser
  • datacenter_asn — eine Rechenzentrums- oder Cloud-Adresse
  • device_many_accounts — drei oder mehr Ihrer Konten auf einem Gerät innerhalb von 24 Stunden (wenn Sie accountId senden; aus Erlauben wird Prüfen, nie Blockieren)
  • disposable_email — eine Wegwerf-E-Mail-Adresse
  • emulator_detected — ein Emulator
  • gpu_inconsistent — der WebGL-Bericht des Browsers widerspricht sich selbst und nennt eine GPU, die er nicht haben kann (Browser-Konsistenz-Beta; nur review)
  • high_activity_device — ein ungewöhnlich oft gesehenes Gerät (weiches Signal)
  • ip_blocklisted — eine Adresse auf einer Blockliste
  • ip_many_accounts — drei oder mehr Ihrer Konten von einer Adresse innerhalb von 24 Stunden; geteilte Ausgänge wie VPNs und Mobilfunkanbieter zählen nie (wenn Sie accountId senden; nur zur Information)
  • multi_account_device — ein Gerät, das bereits hinter mehreren Ihrer Konten steckt
  • privacy_relay — Apples iCloud Private Relay, anstelle von proxy_detected (allein: review)
  • private_browsing — ein Inkognito- oder privates Fenster
  • proxy_detected — ein Residential- oder Cloud-Proxy
  • timezone_mismatch — die Zeitzone des Browsers widerspricht der Adresse (nur wenn geprüft)
  • tor_exit_node — ein Tor-Exit
  • virtual_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.

TokenVorgetäuschter BesucherSignaleEntscheidung
test_clean203.0.113.42 · USnichtsallow
test_vpn198.51.100.18 · NLVPN, Rechenzentrum (PROTON_VPN)review
test_proxy203.0.113.9 · DEProxy, Rechenzentrum (BRIGHT_DATA)block
test_datacenter203.0.113.7 · DERechenzentrum (AWS)allow
test_tor203.0.113.99 · nullTor (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 Tarif10.000 Besucherprüfungen / Monat · bis zu 1.000 / Stunde
Starter50.000 / Monat · bis zu 5.000 / Stunde
Growth250.000 / Monat · bis zu 20.000 / Stunde
Scale1.000.000 / Monat · bis zu 50.000 / Stunde
EnterpriseVertraglich vereinbart
Öffentliches InteresseKein Monatskontingent, kein Stundenlimit
Pro Quell-IP200.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 30. November 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 mit degraded: true beantwortet.
  • 400 Bad Request auf /v1/lookup/{ip} — der Pfad enthält nicht genau eine öffentliche IPv4- oder IPv6-Adresse: Invalid IP format. oder Private or reserved IP — nothing to look up. Die zweite Antwort kommt auch für eine IPv4-gemappte Adresse (::ffff: gefolgt von der IPv4-Adresse): Senden Sie nur die IPv4-Adresse.
  • 401 Unauthorized — Ungültiger oder fehlender API-Schlüssel. Missing Key. kommt auch, wenn der Header Authorization: Bearer lautet und nichts darauf folgt: Die Variable mit Ihrem Schlüssel war dort leer, wo der Aufruf lief (zum Beispiel ist MASKBREAK_API_KEY auf diesem Server oder in dieser Shell nicht gesetzt). Node sendet eine nicht gesetzte Variable als das Wort undefined; darauf lautet die Antwort Invalid API Key. Auf /v1 nennt das Feld hint die wahrscheinliche Ursache.
  • 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 hint nennt die aufrufende Adresse). Von derselben Adresse nicht wiederholbar.

    Dritte Ursache, und die verwirrende: Ein bloßes error code: 1010 als text/plain ohne JSON-Body kommt nicht von uns — das ist unser Edge, der den Standard-User-Agent Ihres HTTP-Clients ablehnt. Python-urllib ist der häufige Fall. Setzen Sie irgendeinen echten User-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 sein Retry-After zä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 Sie Retry-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 code storage_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 nach Retry-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, Apples iCloud Private Relay (review), 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; Geheimnis ersetzen erzeugt dort ein neues: Zustellungen werden sofort damit signiert, also tragen Sie es gleich danach in Ihrem Empfänger ein, und ersetzen Sie es, sobald jemand, der es gesehen hat, keine Warnungen mehr signieren können soll). 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
    "privacy_relay": false,          // since 10 Oct 2026; true = Apple's iCloud Private Relay (review), proxied stays true
    "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.

Bereit für echten Traffic?
Kostenloser Schlüssel in unter einer Minute — 10.000 Prüfungen im Monat, keine Karte.
Kostenlos starten Live ausprobieren
FAQ

Häufige Fragen

Wie authentifiziere ich mich bei der Maskbreak-API?
Übergeben Sie Ihren API-Schlüssel als Bearer-Token im Authorization-Header: Authorization: Bearer YOUR_API_KEY. API-Schlüssel werden nach der Registrierung im Dashboard erzeugt, einer pro Konto.
Wie schnell antwortet die API?
Jede Auswertung meldet ihre serverseitige Verarbeitungszeit in evaluated_in_ms. Die Gesamtlatenz hängt auch vom Netzwerk und den verwendeten Datenquellen ab. Setzen Sie im Backend ein Zeitlimit, messen Sie Ihren eigenen Datenverkehr und legen Sie das Vorgehen bei Zeitüberschreitungen oder unvollständigen Signalen fest.
Welche Sprachen und SDKs werden unterstützt?
Offizielle SDKs gibt es für Node.js (@sentinelsup/sdk), Python (sentinelsup) und PHP (sentinelsup/sdk auf Packagist), dazu einen gehosteten MCP-Server für KI-Agenten. Die REST-API lässt sich direkt aus jeder HTTP-fähigen Umgebung aufrufen, einschließlich Go, PHP, Ruby, Java, Rust, C#, Kotlin und Swift.
Was ist im kostenlosen Tarif enthalten?
10.000 Besucherprüfungen im Monat (bis zu 1.000 pro Stunde), ohne Kreditkarte. Jeder Endpunkt und jedes Signal ist im kostenlosen Tarif enthalten — VPN-Erkennung, Residential-Proxy-Scoring, Antidetect-Browser-Erkennung und Bot-Fingerprinting. Bezahlte Tarife erhöhen das Kontingent: Starter 29 €, Growth 99 € und Scale 299 € im Monat, zzgl. MwSt.
Was passiert, wenn ich mein Ratenlimit überschreite?
Die API liefert HTTP 429 mit einem Retry-After-Header. Zwei Tariflimits gelten: das Stundenlimit (Retry-After sagt, wann das Stundenfenster zurückgesetzt wird) und das Monatskontingent (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.