Shield · Server verification

Server-side verification

The browser SDK provides signals, but browser payloads can be spoofed — use server-side verification for enforcement decisions. Secret API keys never belong in client-side code.

Endpoint

Secret-key authenticated

POST https://shield.findip.net/v1/shield/sessions/verify
// Request
{
  "session_id": "sess_xxx",
  "event": "signup_attempt"
}

// Response
{
  "verified": true,
  "risk_status": "available",
  "risk_score": 87,
  "recommendation": "challenge",
  "challenge_passed": false,
  "challenge_passed_at": null,
  "session": {
    "visitor_id": "vis_xxx",
    "first_seen_at": "2026-09-08T10:00:00.000Z",
    "last_seen_at": "2026-09-08T10:05:00.000Z",
    "event_count": 4,
    "pageview_count": 2,
    "risk_reasons": ["vpn_detected", "payment_attempt"],
    "ip_rotating": true,
    "asn_rotating": false,
    "country_rotating": false,
    "distinct_ips": 2,
    "distinct_asns": 1,
    "distinct_countries": 1,
    "ips": ["203.0.113.7", "203.0.113.9"],
    "countries": ["IL"],
    "last_ip": "203.0.113.9",
    "last_country": "IL",
    "last_country_name": "Israel",
    "last_asn": 1680,
    "last_asn_name": "Partner",
    "last_organization": "Partner Communications",
    "network": {
      "vpn": true, "proxy": false, "tor": false, "relay": false,
      "hosting": false, "datacenter": false, "malicious": false
    }
  }
}

session is the picture Shield has of the whole session, for your own records — an order notification, a fraud queue, a CRM note. risk_reasons are the distinct reasons across every event; ip_rotating, asn_rotating and country_rotating are true when the session was seen from more than one IP, network or country; network flags are true if any event of the session came from that kind of address; last_* describe the most recent event. ips holds at most 10 addresses. Identity is not included — your backend already knows who its customer is.

challenge_passed is true once the visitor completed a Cloudflare Turnstile challenge that Shield verified for this session (see In-page enforcement). Accept challenge only when it is true and reject block to turn in-page friction into a decision no client can bypass. risk_status is unknown when no event of the session had IP intelligence; treat it as monitor, never as safe.

Recommended flow

1. BrowserSDK tracks signup_attempt and receives the initial risk signal.
2. Your backendReceives the signup request including the session_id.
3. VerifyBackend calls /v1/shield/sessions/verify with your secret API key.
4. EnforceAllow, challenge, or block based on the verified response.

Integration example

// Frontend: include session ID in the signup request
const { sessionId } = FindIP.getSession();
await fetch('/api/signup', {
  method: 'POST',
  body: JSON.stringify({ email, password, findip_session_id: sessionId }),
});

// Backend: verify before creating the account
const verify = await fetch('https://shield.findip.net/v1/shield/sessions/verify', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FINDIP_SHIELD_SECRET_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    session_id: req.body.findip_session_id,
    event: 'signup_attempt',
  }),
});