HomeAPI DocsBlogContact
Log InGet Started
Developer Resources

Integrations & SDKs

Add fraud detection to any stack in under 5 minutes. Works with any language that can make HTTP requests — no proprietary SDK required.

< 40ms
Average global response time
Any lang
REST API works with any HTTP client
No SDK
Plain HTTP POST, zero dependencies

One endpoint. Full signal set.

A single POST request returns a risk score, verdict, and all detection signals. No pagination, no setup, no webhooks required to get started.

Endpoint
Method POST
URL https://api.sntlhq.com/v1/check
Auth Authorization: Bearer YOUR_API_KEY
Request { "ip": "1.2.3.4", "user_agent": "...", "headers": {} }
Response { "risk_score": 94, "verdict": "block", "signals": { "vpn": true, "proxy": false, "tor": false, "datacenter": false, "residential_proxy": true, "headless": false }, "country": "US", "latency_ms": 28 }

Pick your language

Copy-paste ready examples for the most common server-side languages. All examples show the full request/response flow with a block-or-pass pattern.

Node.js / JavaScript
const response = await fetch('https://api.sntlhq.com/v1/check', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    ip: req.ip,
    user_agent: req.headers['user-agent'],
    headers: req.headers
  })
});
const result = await response.json();

if (result.verdict === 'block') {
  return res.status(403).json({ error: 'Access denied' });
}
Python
import requests

response = requests.post(
    'https://api.sntlhq.com/v1/check',
    headers={
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json'
    },
    json={
        'ip': request.remote_addr,
        'user_agent': request.headers.get('User-Agent'),
        'headers': dict(request.headers)
    }
)
result = response.json()

if result['verdict'] == 'block':
    return jsonify({'error': 'Access denied'}), 403
PHP
$response = file_get_contents('https://api.sntlhq.com/v1/check', false, stream_context_create([
    'http' => [
        'method'  => 'POST',
        'header'  => "Authorization: Bearer YOUR_API_KEY\r\nContent-Type: application/json\r\n",
        'content' => json_encode([
            'ip'         => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'headers'    => getallheaders()
        ])
    ]
]));
$result = json_decode($response, true);

if ($result['verdict'] === 'block') {
    http_response_code(403);
    echo json_encode(['error' => 'Access denied']);
    exit;
}
Go
type CheckRequest struct {
    IP        string            `json:"ip"`
    UserAgent string            `json:"user_agent"`
    Headers   map[string]string `json:"headers"`
}

func checkIP(ip, userAgent string) (*SentinelResult, error) {
    body, _ := json.Marshal(CheckRequest{IP: ip, UserAgent: userAgent})
    req, _ := http.NewRequest("POST", "https://api.sntlhq.com/v1/check", bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()

    var result SentinelResult
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}
Ruby
require 'net/http'
require 'json'

uri  = URI('https://api.sntlhq.com/v1/check')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.path)
request['Authorization'] = 'Bearer YOUR_API_KEY'
request['Content-Type']  = 'application/json'
request.body = {
  ip:         request.remote_ip,
  user_agent: request.user_agent,
  headers:    request.headers.to_h
}.to_json

response = http.request(request)
result   = JSON.parse(response.body)

render json: { error: 'Access denied' }, status: 403 if result['verdict'] == 'block'
Java
HttpClient client = HttpClient.newHttpClient();
String body = String.format(
    "{\"ip\":\"%s\",\"user_agent\":\"%s\"}",
    request.getRemoteAddr(),
    request.getHeader("User-Agent")
);

HttpRequest httpRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.sntlhq.com/v1/check"))
    .header("Authorization", "Bearer YOUR_API_KEY")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(body))
    .build();

HttpResponse<String> response = client.send(httpRequest,
    HttpResponse.BodyHandlers.ofString());
JSONObject result = new JSONObject(response.body());

if ("block".equals(result.getString("verdict"))) {
    response.setStatus(403);
    return Map.of("error", "Access denied");
}

Works with every framework

If your framework can make an outbound HTTP request, Sentinel works. No middleware, no plugins, no vendor lock-in.

NX
Next.js
Node / React
EX
Express.js
Node
FY
Fastify
Node
NS
NestJS
Node / TS
DJ
Django
Python
FA
FastAPI
Python
FL
Flask
Python
LV
Laravel
PHP
SF
Symfony
PHP
RR
Rails
Ruby
SB
Spring Boot
Java
GN
Gin
Go
EC
Echo
Go

Integrate with your platform

Step-by-step guides for Shopify, Stripe, iOS, and Android.

SH
Shopify
Protect checkout from card testing and bot signups

Step 1: Add the Sentinel SDK to your theme. In Shopify Admin → Online Store → Themes → Edit code → theme.liquid, add before </head>:

Liquid / HTML
<script async src="https://fa.sntlhq.com/agent"></script>

Step 2: Create a serverless function (Shopify Functions or an external endpoint) that calls Sentinel before order creation:

Node.js — Shopify Webhook
// Called on orders/create webhook
const sentinel = await fetch('https://sntlhq.com/v1/evaluate', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SENTINEL_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ token: req.body.sentinelToken })
});
const { isSuspicious } = await sentinel.json();
// If suspicious → flag order for review or cancel
if (isSuspicious) flagOrderForReview(order.id);
ST
Stripe
Block fraudulent payments before they hit your Stripe account

Check the customer with Sentinel before confirming a PaymentIntent. If suspicious, cancel the payment before it processes:

Node.js — Stripe Integration
const stripe = require('stripe')(process.env.STRIPE_SECRET);

app.post('/create-payment', async (req, res) => {
  // 1. Check with Sentinel first
  const check = await fetch('https://sntlhq.com/v1/evaluate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SENTINEL_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ token: req.body.sentinelToken })
  });
  const { isSuspicious } = await check.json();

  if (isSuspicious) {
    return res.status(403).json({ error: 'Payment blocked' });
  }

  // 2. Safe — create the PaymentIntent
  const intent = await stripe.paymentIntents.create({
    amount: req.body.amount,
    currency: 'usd',
    metadata: { sentinel_checked: 'true' }
  });
  res.json({ clientSecret: intent.client_secret });
});
SW
iOS (Swift)
Detect jailbroken devices and emulators from your iOS app

Call the Sentinel API from your backend when your iOS app submits a signup or login. The API detects VPNs, proxies, and suspicious devices:

Swift — URLSession
func checkWithSentinel(token: String) async throws -> Bool {
    var request = URLRequest(
        url: URL(string: "https://sntlhq.com/v1/evaluate")!
    )
    request.httpMethod = "POST"
    request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONEncoder().encode(["token": token])

    let (data, _) = try await URLSession.shared.data(for: request)
    let result = try JSONDecoder().decode(SentinelResponse.self, from: data)
    return result.isSuspicious
}

struct SentinelResponse: Decodable {
    let isSuspicious: Bool
}
KT
Android (Kotlin)
Detect emulators, rooted devices, and proxy traffic from Android

Call the Sentinel API from your Android app's backend. Use OkHttp or Retrofit to evaluate users at signup or login:

Kotlin — OkHttp
suspend fun checkWithSentinel(token: String): Boolean {
    val client = OkHttpClient()
    val json = JSONObject().put("token", token)

    val request = Request.Builder()
        .url("https://sntlhq.com/v1/evaluate")
        .addHeader("Authorization", "Bearer $apiKey")
        .addHeader("Content-Type", "application/json")
        .post(json.toString()
            .toRequestBody("application/json".toMediaType()))
        .build()

    val response = client.newCall(request).execute()
    val body = JSONObject(response.body!!.string())
    return body.getBoolean("isSuspicious")
}

Get your free API key

10,000 requests/month free. No credit card required. Up and running in under 5 minutes.