Backend: Denselben JSON-Body an POST …/humanproof/verify senden. Die Antwort enthält u. a. status, suggestedAction, reasonCode und accept (boolean — nur true, wenn die Eingabe als verifizierte Übermittlung gelten soll). Nur serverseitig aufrufen — der secretKey darf nie im Browser landen.
Manche Formulare verlangen beim Verify ein Feld hostname (muss mit boundHost im Client-Payload übereinstimmen — der Host, mit dem die Challenge angefordert wurde). Ist requireVerifyHostname aktiv und fehlt hostname, liefert die API reasonCode HOSTNAME_REQUIRED. 127.0.0.1 und localhost sind unterschiedlich; tragen Sie beide in hostnames ein oder stimmen Sie Entwicklungs-URL und data-challenge-hostname mit dem Verify-Body ab.
Script- und API-URLs auf dieser Seite folgen Ihrer Konfiguration (Produktion: api.agenthouse.org; humanproof.publicSdkBase in secrets.json für eine Dev-API).
Wenn das SDK beim Absenden Challenge und Proof-of-Work ausführen muss, feuert es auf window das CustomEvent humanproof.securingForm (detail: form, siteKey, formId), damit Ihre App Fortschritt anzeigen kann. Wenn die Absicherung abgeschlossen ist und der signierte Proof angehängt wurde — unmittelbar vor dem nativen Submit — feuert es auf dem Formular agenthouse_humanproof_verified_submit (bubbles; detail: form, siteKey, formId, proof-Metadaten). Dieses Event bedeutet nur erfolgreiche clientseitige Absicherung; verify auf dem Backend bleibt Pflicht. Bei Fehlern: humanproof-error auf dem Formular.
window.addEventListener('humanproof.securingForm', function (e) {
/* e.detail.form, siteKey, formId */
});
form.addEventListener('agenthouse_humanproof_verified_submit', function (e) {
/* e.detail.form, siteKey, formId, proof.challengeId, proof.proof, … */
});
Standardmäßig sperrt das SDK das Formular (input, select, textarea, button — alles, was nicht bereits disabled ist), während Challenge geholt und Proof-of-Work gelöst wird. Das Formular erhält data-humanproof-securing=1 und aria-busy=true für Styling und Barrierefreiheit, und wird kurz vor dem Absenden (oder sofort bei einem Fehler) wieder freigegeben. Abschalten per Script mit data-lock-while-securing=false oder pro Formular mit data-humanproof-lock-while-securing=false.
Proof-of-Work ist erst nach einer Server-Challenge möglich. Optional: data-prefetch-challenge-delay-ms am Script (Millisekunden nach dem Laden), um Challenge und Lösung im Hintergrund zu starten; beim Absenden wird die Lösung wiederverwendet, solange sie gültig ist. data-prefetch-expiry-margin-ms verlangt so viel verbleibende TTL vor der Wiederverwendung (Uhrenabweichung). Pro Formular: data-humanproof-prefetch-challenge-delay-ms und data-humanproof-prefetch-expiry-margin-ms.
HTML
HTML Mehrfach
Node.js
PHP
Python
<form id="contact-form" data-humanproof="YOUR_FORM_ID">
<label>Email <input type="email" name="email" required></label>
<button type="submit">Send</button>
</form>
<script
src="https://api.agenthouse.org/humanproof/humanproof.js"
data-site-key="YOUR_PUBLIC_SITE_KEY"
data-powered-by="true"
data-endpoint="https://api.agenthouse.org/humanproof"
data-prefetch-challenge-delay-ms="2500"
data-prefetch-expiry-margin-ms="15000"
></script>
<form id="a" data-humanproof="form-a">...</form>
<form id="b" data-humanproof="form-b">...</form>
<script
src="https://api.agenthouse.org/humanproof/humanproof.js"
data-site-key="YOUR_PUBLIC_SITE_KEY"
data-powered-by="true"
data-endpoint="https://api.agenthouse.org/humanproof"
data-prefetch-challenge-delay-ms="2500"
data-prefetch-expiry-margin-ms="15000"
></script>
const verifyUrl = 'https://api.agenthouse.org/humanproof/verify';
const secretKey = ''; // set from your secrets store before use
const payload = JSON.parse(formFields.humanproof);
const body = { secretKey, ...payload };
if (typeof body.hostname !== 'string' || !body.hostname.trim()) {
if (typeof body.boundHost === 'string' && body.boundHost.trim()) {
body.hostname = body.boundHost.trim();
}
}
const result = await (await fetch(verifyUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
})).json();
if (!result.accept) return; // do not process as a verified submit
<?php
$url = 'https://api.agenthouse.org/humanproof/verify';
$secret = ''; // set from your server secret store before use
$payload = json_decode($_POST['humanproof'], true);
$body = array_merge(['secretKey' => $secret], $payload);
if (empty($body['hostname']) && !empty($body['boundHost'])) {
$body['hostname'] = $body['boundHost'];
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
]);
$out = curl_exec($ch);
$result = json_decode($out, true);
// if (empty($result['accept'])) { return; }
?>
import json
import urllib.request
VERIFY_URL = "https://api.agenthouse.org/humanproof/verify"
def verify_humanproof(secret_key: str, payload: dict) -> dict:
body = {"secretKey": secret_key, **payload}
if not (isinstance(body.get("hostname"), str) and body["hostname"].strip()):
bh = body.get("boundHost")
if isinstance(bh, str) and bh.strip():
body["hostname"] = bh.strip()
req = urllib.request.Request(
VERIFY_URL,
data=json.dumps(body).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
r = json.load(resp)
# if not r.get("accept"): return
return r