v1 Loading…

API Reference

Integrate invisible bot & proxy detection into your product. No CAPTCHAs. No friction. Under 40ms per evaluation.

Quick Start

From zero to working integration in 5 minutes. You need your API key from the dashboard.

Node.js SDK npm install @sentinelsup/sdk npm ↗ · GitHub ↗
OpenAPI 3.1 https://sntlhq.com/openapi.json Spec ↗ · Open in Postman ↗
Your Sentinel API Endpoint
Loading…
Your backend calls this URL. Your API key authenticates the request.
Complete Integration Example
// ——— STEP 1: Add SDK to your HTML <head> —————————————————————————————————————
<script async src="loading..." id="_mcl"></script>
<form class="monocle-enriched" id="login-form">
  <input type="email" name="email" />
  <!-- SDK auto-injects: <input type="hidden" name="monocle" value="TOKEN" /> -->
</form>

// ——— STEP 2: Read token in your frontend JS ———————————————————————————————————
document.getElementById('login-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const sentinelToken = document.querySelector('input[name="monocle"]')?.value;
  // Send to YOUR backend (never call Sentinel directly from the browser)
  await fetch('your-login-endpoint', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, sentinelToken })
  });
});

// ——— STEP 3: Evaluate on your backend (Node.js) ——————————————————————————————
app.post('/your-login-endpoint', async (req, res) => {
  const { email, sentinelToken } = req.body;

  const result = await fetch('LOADING/v1/evaluate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ token: sentinelToken })
  });

  const data = await result.json();

  if (data.isSuspicious) {
    return res.status(403).json({ error: 'Bot or proxy detected.' });
  }

  // ✓ Connection is clean — proceed
  res.json({ success: true });
});

Try It Live

Hit the sandbox endpoint with one click. No signup, no API key — just real /v1/evaluate response shapes you can copy-paste into your code.

Request
GET https://sntlhq.com/v1/evaluate/sample
   ?scenario=clean
No auth required · 30 req/min · returns sample data
Response
awaiting run…
{
  "click ▶ Run Request": "to see live JSON"
}

Production calls use POST /v1/evaluate with a token from the SDK and an Authorization: Bearer sk_live_... header. The sample endpoint returns the same response shape so you can wire your parsing logic before signing up.

How It Works

Sentinel uses a 3-step Client → Your Backend → Sentinel API flow to keep your secret key off the browser.

1
Browser Collection

The Sentinel SDK runs invisibly in your user's browser, collecting telemetry. It injects an encrypted token into your forms.

2
Token Forwarded

Your frontend submits the token to your own backend server along with the rest of the form data.

3
API Evaluation

Your backend calls POST /v1/evaluate with your secret key. Sentinel returns a threat intelligence report instantly.

Authentication

All requests to /v1/evaluate must include your secret API key as a Bearer token. Find your key in the Dashboard.

Authorization: Bearer sk_live_YOUR_SECRET_KEY
Keep your key secret
Never put sk_live_… in your HTML, JavaScript, or any client-side code. Only use it in your backend server environment.
1

Embed the SDK

Add the Sentinel Edge SDK to every page where you want to evaluate users. It runs silently and generates a token. Copy the exact script below — the key is already configured for your account.

HTML — paste inside <head>
<!-- Sentinel Edge SDK — paste inside <head> -->
<script async src="loading..." id="_mcl"></script>

<!-- Add class="monocle-enriched" to any form you want evaluated -->
<form class="monocle-enriched" id="my-form">
  <!-- The SDK injects this automatically: -->
  <input type="hidden" name="monocle" value="eyJ..." />
</form>

The SDK takes ~1–2 seconds to load and generate the token. The hidden input will be empty until then.

2

Collect the Token

When your form is submitted, read the monocle hidden input and forward it to your backend. Guard against the case where the SDK hasn't loaded yet.

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

  // Read the token injected by the Sentinel SDK
  const sentinelToken = document.querySelector('input[name="monocle"]')?.value;

  if (!sentinelToken) {
    // SDK is still loading — ask user to try again
    return showError('Security check loading. Please try again.');
  }

  // Forward 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,
      sentinelToken  // ← pass this to your backend
    })
  });
});
3

Call the Evaluate Endpoint

POST loading…

Call this from your backend server — never from the browser. Pass the token from the client and your secret API key.

Request Body

token string required
The encrypted token from the client's input[name="monocle"] field.
const response = await fetch('LOADING/v1/evaluate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    token: req.body.sentinelToken   // from the form submission
  })
});

const data = await response.json();
// data.isSuspicious → true if VPN/proxy/bot detected
// data.details      → full forensic breakdown

if (data.isSuspicious) {
  return res.status(403).json({ error: 'Suspicious connection.' });
}
import requests

response = requests.post(
    'LOADING/v1/evaluate',
    headers={
        'Authorization': 'Bearer sk_live_YOUR_API_KEY',
        'Content-Type': 'application/json'
    },
    json={
        'token': sentinel_token  # from the form POST body
    }
)

data = response.json()

if data['isSuspicious']:
    return '403 Suspicious connection', 403
curl -X POST 'LOADING/v1/evaluate' \
  -H 'Authorization: Bearer sk_live_YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"token": "eyJ..."}'
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL            => 'LOADING/v1/evaluate',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer sk_live_YOUR_API_KEY',
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS     => json_encode([
        'token' => $_POST['sentinelToken']
    ])
]);
$data = json_decode(curl_exec($ch), true);
if ($data['isSuspicious']) { http_response_code(403); exit(); }

Response Object

A successful call returns 200 OK with this JSON structure.

Field Type Description
decision string Recommended action: "allow", "review", or "block".
risk_score integer Composite risk score 0–100 across all network and device signals.
isSuspicious boolean Master flag. true if any threat signal fired (VPN, proxy, datacenter, Tor, antidetect, automation, emulator).
ip string The unmasked, real IP address of the user. Mirrored at details.ip.
country string ISO 3166-1 country code (e.g. "US", "EE"). Mirrored at details.cc.
network.vpn boolean Commercial VPN detected (NordVPN, Proton, ExpressVPN, etc.). Mirrored at details.vpn.
network.proxy boolean Residential or SOCKS/HTTP proxy detected. Mirrored at details.proxied.
network.datacenter boolean Traffic from a known datacenter / cloud provider ASN. Mirrored at details.dch.
network.tor boolean Tor exit node detected. Mirrored at details.tor.
network.anonymous boolean Any anonymization layer detected. Mirrored at details.anon.
network.residential boolean true when neither datacenter nor proxy fired (looks like a real home IP).
network.service string Identified provider when known (e.g. "PROTON_VPN", "BRIGHT_DATA"). Mirrored at details.service.
device.antidetect boolean Antidetect / fingerprint-spoofing browser (Multilogin, Kameleo, GoLogin, etc.). Only present when fingerprintEventId was sent.
device.automation boolean Automated browser detected (Puppeteer, Playwright, Selenium).
device.emulator boolean Mobile emulator.
device.virtual_machine boolean Virtual machine.
device.visitor_id string Stable device fingerprint hash. Same device returns the same id across sessions even after cookies clear — useful for ATO defense and device clustering.
device.tampering_score number 0–1 tampering score. Above 0.6 strongly suggests an antidetect browser; 0.3–0.6 means soft inconsistencies; below 0.3 is normal.
reasons string[] Machine-readable codes for which signals fired (e.g. "vpn_detected", "datacenter_asn", "antidetect_browser").
evaluated_in_ms integer Server-side processing time for this request, in milliseconds.
200 OK — Suspicious connection
{
  "status": "success",
  "isSuspicious": true,
  "decision": "review",
  "risk_score": 71,
  "ip": "185.107.80.12",
  "country": "EE",
  "network": {
    "vpn": true,
    "proxy": false,
    "datacenter": true,
    "tor": false,
    "anonymous": true,
    "residential": false,
    "service": "PROTON_VPN"
  },
  "reasons": ["vpn_detected", "datacenter_asn"],
  "evaluated_in_ms": 28,
  "details": { /* legacy mirror */ "ip": "185.107.80.12", "cc": "EE", "vpn": true, "proxied": false, "anon": true, "dch": true, "service": "PROTON_VPN" }
}
200 OK — Clean connection
{
  "status": "success",
  "isSuspicious": false,
  "decision": "allow",
  "risk_score": 3,
  "ip": "82.131.45.9",
  "country": "DE",
  "network": {
    "vpn": false,
    "proxy": false,
    "datacenter": false,
    "tor": false,
    "residential": true,
    "service": "DEUTSCHE_TELEKOM"
  },
  "reasons": [],
  "evaluated_in_ms": 22
}

Rate Limits

Sentinel is in open beta — all access is free. Two limits run in parallel; whichever you hit first returns 429.

Scope Limit
Per API key1,000 requests / hour
Per source IP500 requests / hour
EnterpriseCustom — contact us

No monthly cap during beta. The per-IP cap throttles abuse when a single key gets shared across many machines. Retry-After is included on every 429 response.

Error Codes

All error responses include an error string field.

  • 400 Bad Request — Missing or invalid token.
  • 401 Unauthorized — Invalid or missing API key.
  • 429 Rate Limited — Monthly quota exceeded.
  • 500 Server Error — Token tampered with or expired.