API-Referenz

Endpunkte, Antwortfelder, Statuscodes, Rate-Limits und unsere Zusagen zur Stabilität.

API-Referenz (V2)

Basis-URL: https://api.captchacore.eu — alle Endpunkte antworten mit JSON. Wer das mitgelieferte Widget einsetzt, ruft /challenge nie selbst auf; das übernimmt das Widget. Für ein eigenes Plugin ist nur /verify Pflicht.

Authentifizierung

Endpunkt Schlüssel Wo verwendet
GET /api/v2/challenge cc_pub_… im Browser — darf öffentlich sein
POST /api/v2/verify cc_sec_… nur auf dem Server — niemals ausliefern
GET /api/v2/status cc_sec_… nur auf dem Server

Der Schlüssel gehört immer in den Header X-CaptchaCore-Key, nie in die URL.

GET /api/v2/challenge

Fordert eine Rechenaufgabe samt Policy an. Optionaler Query-Parameter: form_type (siehe Form-Policies). Antwortet mit HTTP 200.

{
  "challenge_id": "ch_a45cd15b0d6d2dc6",
  "nonce": "a45cd15b…1d50",          // einmalig, 5 Minuten gültig
  "expires_at": 1788860066,
  "policy": {
    "mode": "adaptive",
    "challenge_type": "sha256_pow",     // oder argon2id_pow, interaction_step_up
    "challenge_types": ["sha256_pow"],
    "difficulty": 4,                  // führende Nullen im Hash
    "algorithm": "sha256",
    "time_budget_ms": 500,
    "requires_interaction": false,
    "requires_stepup_on_submit": false,
    "memory_hard_enabled": false,
    "form_type": "contact",
    "site_profile": "balanced",
    "uam_level": 0              // 0–3, Under-Attack-Stufe
  },
  "bindings": {                       // bindet den Token an Site, Origin und Formular
    "site_id": 2,
    "origin": "https://example.com",
    "form_type": "contact",
    "issued_at": 1788859766,
    "expires_at": 1788860066,
    "token_ttl_sec": 300
  },
  "bindings_signature": "CBkATjVc…RDg==",  // Ed25519, unveränderlich mitschicken
  "signing_key_id": "k_397d99d39156c5f5",
  "widget": { /* im Admin gepflegtes Erscheinungsbild */ },
  "widget_version": "3.0.0"
}

Wichtig für eigene Clients: bindings und bindings_signature müssen unverändert in den Token zurückwandern. Fehlen sie oder wurden sie verändert, antwortet /verify mit block.

POST /api/v2/verify

Prüft den Token aus dem Formular. Antwortet im Regelfall mit HTTP 200 — auch bei einer Ablehnung. Werten Sie den Body aus, nicht den Statuscode.

// Request
{
  "token": "<Wert des Feldes captchacore_token>",
  "form_type": "contact",      // optional, steuert die Policy
  "page_url": "https://…"       // optional, nur für Auswertungen
}

// Response
{
  "valid": true,               // false bei block UND bei step_up
  "decision": "allow",        // allow | challenge | step_up | block
  "action": "allow",          // identisch zu decision, für V1-Kompatibilität
  "request_id": "…",          // für Rückfragen an den Support
  "risk_score": 8,             // 0–100, höher = riskanter
  "confidence": 0.94,          // 0.0–1.0 Signalabdeckung
  "reasons": ["pow_valid", "behavior_human_like"],
  "step_up": null             // bei decision=step_up: Daten für die Zusatzprüfung
}

Wie Sie decision behandeln

decision valid Empfohlenes Verhalten
allow true Formular normal verarbeiten.
challenge true Durchlassen, aber protokollieren. Optional eine zusätzliche eigene Prüfung, etwa eine Bestätigungsmail.
step_up false NICHT hart ablehnen. Das Widget führt die Zusatzprüfung selbst durch; geben Sie dem Nutzer das Formular mit einer freundlichen Meldung zurück.
block false Ablehnen. Fehlermeldung ohne Details anzeigen, damit Angreifer nichts lernen.

GET /api/v2/status

Erreichbarkeit und aktuelle Under-Attack-Stufe der Site. Eignet sich für einen Health-Check im Plugin.

{ "status": "ok", "under_attack": false, "version": "2.x" }

HTTP-Statuscodes

Code Body Ursache und Reaktion
200 {"valid": …} Normalfall, auch bei Ablehnung. Body auswerten.
401 {"error":"Missing API key"} Header X-CaptchaCore-Key fehlt.
401 {"error":"Invalid API key"} Schlüssel falsch, widerrufen oder für die falsche Site. Auch der Fall, wenn der öffentliche Schlüssel auf /verify verwendet wird.
403 {"error": …} Origin gehört nicht zu den erlaubten Domains der Site.
422 {"message": …} Pflichtfeld fehlt, meist token.
429 Rate-Limit erreicht. Wie einen Ausfall behandeln, siehe Fehlerbehandlung.
5xx Störung auf unserer Seite. Fail-Open- oder Fail-Closed-Regel greift.

Rate-Limits

Endpunkt Grenze Gezählt nach
GET /api/v2/challenge60 / minIP des Besuchers
POST /api/v2/verify600 / minSite-Key des Kunden

Verify wird pro Site gezählt, nicht pro IP. Ihr Server darf also so viele Formulare prüfen, wie Ihre Site an Verkehr hat, ohne sich selbst auszusperren.

Webhooks

CaptchaCore benachrichtigt Ihre eigenen Systeme, wenn etwas passiert, auf das Sie reagieren wollen: erreichtes Kontingent, ausgelöster Under-Attack-Mode, gesperrtes oder freigeschaltetes Konto. Eingerichtet wird das im Kundenbereich unter Sites → Webhooks (ab Tarif Professional).

Ereignisse

EreignisWann
usage.warningMonatskontingent zu 80 % bzw. 90 % ausgeschöpft
usage.limit_reachedMonatskontingent erreicht — weitere Verifikationen werden abgewiesen
site.under_attack.activatedUnder-Attack-Mode einer Site aktiviert (manuell oder automatisch)
site.under_attack.deactivatedUnder-Attack-Mode einer Site beendet
organisation.blockedKonto gesperrt (z. B. offene Rechnung) — API-Aufrufe werden abgewiesen
organisation.unblockedKonto wieder freigeschaltet
webhook.testTestereignis aus dem Kundenbereich

Was ankommt

Ein POST mit JSON-Body. Drei Header helfen beim Zuordnen und Prüfen:

POST /ihr-endpunkt HTTP/1.1
Content-Type: application/json
X-CaptchaCore-Event: usage.limit_reached
X-CaptchaCore-Delivery: 5f1c2c1e-…            // eindeutig je Zustellung, gleich bei Wiederholungen
X-CaptchaCore-Timestamp: 1758196800
X-CaptchaCore-Signature: sha256=3b2a…

{
  "event": "usage.limit_reached",
  "occurred_at": "2026-09-18T14:00:00+02:00",
  "organisation_id": 42,
  "data": { "threshold_percent": 100, "used": 10000, "limit": 10000, "plan": "pro", "period": "2026-09" }
}

Signatur prüfen

Die Signatur ist ein HMAC-SHA256 über „Timestamp.Body" mit dem Schlüssel aus dem Kundenbereich. Vergleichen Sie zeitkonstant und lehnen Sie Zeitstempel ab, die älter als fünf Minuten sind — das verhindert Wiederholungen abgefangener Anfragen.

$secret    = getenv('CAPTCHACORE_WEBHOOK_SECRET');
$body      = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_CAPTCHACORE_TIMESTAMP'] ?? '';
$expected  = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);

if (abs(time() - (int) $timestamp) > 300
    || ! hash_equals($expected, $_SERVER['HTTP_X_CAPTCHACORE_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;
}

$event = json_decode($body, true);
// … verarbeiten, dann schnell mit 2xx antworten
http_response_code(204);

Zustellung und Wiederholung

  • Antworten Sie innerhalb von 5 Sekunden mit einem 2xx-Status — verarbeiten Sie aufwendige Dinge asynchron.
  • Bei Fehlern oder Timeout wird bis zu dreimal zugestellt (nach 30 Sekunden und nach 5 Minuten), mit derselben Delivery-ID — machen Sie Ihre Verarbeitung damit idempotent.
  • Nach zehn Fehlschlägen in Folge wird der Webhook abgeschaltet; im Kundenbereich sehen Sie den Grund und können ihn wieder einschalten.
  • Jede Zustellung mit Antwortcode, Dauer und Antwortauszug steht unter „Zustellungen" — auch für die Fehlersuche auf Ihrer Seite.

Versionierung und Stabilität

Wer ein Plugin pflegt, muss wissen, worauf er sich verlassen kann. Das sind unsere Zusagen.

Die Pfad-Version bleibt stabil

Innerhalb von /api/v2 entfernen und benennen wir keine Antwortfelder um. Neue Felder können jederzeit hinzukommen — lesen Sie Antworten deshalb tolerant und brechen Sie nicht bei unbekannten Schlüsseln ab.

V1 bleibt vorerst erreichbar

Die alte Schnittstelle unter /api/v1 wird weiter bedient, erhält aber keine neuen Signale mehr. Neue Integrationen sollten ausschließlich V2 verwenden.

Das Widget aktualisiert sich selbst

Über den CDN-Pfad captchacore-v2.min.js erhalten Sie immer die gepflegte Fassung. Binden Sie keine eigene Kopie ein, sonst verpassen Sie Verbesserungen an der Bot-Erkennung.

Änderungen kündigen wir an

Alles, was bestehende Integrationen betreffen könnte, sagen wir vorher über den öffentlichen Systemstatus und per E-Mail an die hinterlegte technische Adresse an.