Get API key
← Code samples
Forms

Shield

Add invisible bot protection to any web form — embed the widget, then verify the signed token on your server before processing the submission.

Shield protects web forms from bots without requiring any user interaction. It runs a proof-of-work challenge and behavioural analysis in the background, then issues a signed token your server verifies before processing the submission. You'll need two keys from your Shield site settings: a public site key for the widget, and a private secret key for server-side verification.

Step 1 — Embed the widget

Add the widget script to your <head>:

HTML
<script src="https://app.verifence.io/shield/widget.js" async></script>

Drop a .shield-widget element into your form. The script auto-renders every .shield-widget on the page and injects a hidden field named shield-token, which it populates with a signed token before the form is submitted — you don't add the token field yourself:

HTML
<form method="POST" action="/contact">
  <input type="text" name="name" placeholder="Name" />
  <input type="email" name="email" placeholder="Email" />
  <textarea name="message" placeholder="Message"></textarea>

  <!-- Renders invisibly and injects a hidden "shield-token" field -->
  <div class="shield-widget" data-sitekey="your_site_key"></div>

  <button type="submit">Send</button>
</form>
No Shield.init() call is needed — the widget auto-renders on load. If you render forms dynamically, call Shield.render('.shield-widget') after inserting them, or Shield.execute('your_site_key') to fetch a token programmatically.

Step 2 — Verify on your server

Call the siteverify endpoint after receiving a submission, before doing any processing. It authenticates with your secret key in the body — not the API-KEY header — and does not consume scan credits.

HTTP
POST https://app.verifence.io/api/shield/siteverify
Content-Type: application/json

Request fields

FieldRequiredDescription
secretYesYour Shield site's secret key
tokenYesThe value of the injected shield-token field
emailNoEmail from the form — runs your email rules
textNoFree-text field — returns a spam score if spam detection is enabled
honeypotNoValue of an optional honeypot field you add — a non-empty value is treated as a bot

Response fields

FieldTypeDescription
successbooleantrue if the submission passed all checks — the hard signal
scoreintegerRisk score 0–100 (higher = more likely human); informational
spam_scorefloat0–1 spam probability (only when text is passed and spam detection is on)
languagestringDetected language of the text (e.g. en)
geoobjectCountry and ASN of the submitter's IP, when available
degradedbooleantrue if Shield was in fallback mode — treat as a pass
errorstringPresent when success is false — the reason

cURL

cURL
curl -X POST https://app.verifence.io/api/shield/siteverify \
  -H "Content-Type: application/json" \
  -d '{
    "secret": "your_secret_key",
    "token": "<token from form>",
    "email": "user@example.com",
    "text": "Hello, I am interested in your services."
  }'
JSON — human
{
  "success": true,
  "score": 91,
  "spam_score": 0.03,
  "language": "en",
  "geo": { "country": "US", "asn": "AS15169" }
}
JSON — rejected
{
  "success": false,
  "error": "Verification failed"
}

PHP

PHP
function verifyShieldToken(string $token, string $email = null, string $text = null): array
{
    $payload = [
        'secret' => getenv('VERIFENCE_SHIELD_SECRET'),
        'token'  => $token,
    ];
    if ($email) $payload['email'] = $email;
    if ($text)  $payload['text']  = $text;

    $ch = curl_init('https://app.verifence.io/api/shield/siteverify');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_TIMEOUT        => 5,
    ]);

    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true) ?? [];
}

// Usage — contact form handler
$result = verifyShieldToken(
    token: $request->input('shield-token', ''),
    email: $request->input('email'),
    text:  $request->input('message'),
);

if (!($result['success'] ?? false)) {
    return back()->withErrors(['form' => 'Verification failed. Please try again.']);
}
if (isset($result['spam_score']) && $result['spam_score'] > 0.85) {
    return back()->withErrors(['message' => 'Your message was flagged as spam.']);
}

sendContactEmail($request->all());

Node.js

Node.js
async function verifyShieldToken({ token, email, text } = {}) {
  const payload = { secret: process.env.VERIFENCE_SHIELD_SECRET, token };
  if (email) payload.email = email;
  if (text)  payload.text  = text;

  const res = await fetch('https://app.verifence.io/api/shield/siteverify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(5000),
  });

  if (!res.ok) {
    throw new Error(`Shield siteverify error: ${res.status}`);
  }
  return res.json();
}

// Usage — Express contact form route
app.post('/contact', async (req, res) => {
  const result = await verifyShieldToken({
    token: req.body['shield-token'],
    email: req.body.email,
    text:  req.body.message,
  });

  if (!result.success) {
    return res.status(422).json({ error: 'Verification failed. Please try again.' });
  }
  if (result.spam_score > 0.85) {
    return res.status(422).json({ error: 'Your message was flagged as spam.' });
  }

  await sendContactEmail(req.body);
  res.json({ success: true });
});

Python

Python
import os
import requests

def verify_shield_token(token: str, email: str = None, text: str = None) -> dict:
    payload = {"secret": os.environ["VERIFENCE_SHIELD_SECRET"], "token": token}
    if email:
        payload["email"] = email
    if text:
        payload["text"] = text

    response = requests.post(
        "https://app.verifence.io/api/shield/siteverify",
        json=payload,
        timeout=5,
    )
    response.raise_for_status()
    return response.json()


# Usage — Django or Flask contact form view
result = verify_shield_token(
    token=request.POST.get("shield-token", ""),
    email=request.POST.get("email", ""),
    text=request.POST.get("message", ""),
)

if not result.get("success"):
    return error_response("Verification failed. Please try again.")
if result.get("spam_score", 0) > 0.85:
    return error_response("Your message was flagged as spam.")

send_contact_email(request.POST)

Tips

  • Always check success first. It is the hard signal — reject the submission whenever it is false.
  • A missing or empty token fails open. If the widget didn't load (ad blocker, slow connection) the token is empty, and siteverify returns { "success": true, "degraded": true } rather than blocking. To fail closed instead, treat an empty token as a rejection before calling siteverify.
  • Don't block on score alone. score is informational; success: false is the decision. Use spam_score as an extra filter when spam detection is enabled.
  • Keep the secret key server-side. Never expose it in frontend code.

Ready to build it?

Create a free account and grab an API key in minutes.

Get started free