loader

Sentinel API Reference

Query IP reputation, verify human-proof tokens, and embed the self-hosted captcha widget. Everything you need to integrate Sentinel.

Authentication

Authenticated API endpoints live under /api/v1 and are authenticated with an API key sent in the X-Api-Key request header. Every request is scoped to the key's owner, plan and quota.

Header

X-Api-Key: rdy_live_xxxxxxxxxxxxxxxxxxxxxxxx
Create and manage API keys and Sentinel sites in the Lab. Sentinel API access is available on Dominion and higher plans.

Identity & quota

GET /api/v1/me

Returns the calling API key's identity, plan, remaining credits and quota. Useful for confirming a key works and checking limits before a batch of calls.

Example request

curl https://redeyed.com/api/v1/me \
  -H "X-Api-Key: rdy_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Example response

{
  "key": {
    "name": "Production",
    "prefix": "rdy_live_xxxx",
    "scopes": ["ip:read", "verify"],
    "last_used_at": "2026-06-22T01:14:09Z"
  },
  "plan": "dominion",
  "credit_balance": 9820,
  "quota": 48000
}

IP reputation

GET /api/v1/ip/{ip?}

Scores an IP address. Omit {ip} to score the caller's own IP. Returns a 0–100 score, a risk band, flags (proxy / VPN / Tor / datacenter / bot / abuser), geolocation and ASN. Each lookup is recorded as a behavioral signal for the reputation engine.

Path parameters

ParamTypeDescription
ipstring (optional)IPv4 or IPv6 address. If omitted, the caller's IP is scored. Invalid input returns 422.

Example request

curl https://redeyed.com/api/v1/ip/8.8.8.8 \
  -H "X-Api-Key: rdy_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Example response

{
  "ip": "8.8.8.8",
  "score": 72,
  "risk": "medium",
  "flags": {
    "is_proxy": false,
    "is_vpn": false,
    "is_tor": false,
    "is_datacenter": true,
    "is_bot": false,
    "is_abuser": false
  },
  "country_code": "US",
  "asn": 15169,
  "asn_org": "Google LLC",
  "signals": { /* score breakdown */ },
  "confidence": 61,
  "last_seen_at": "2026-06-22T01:10:00Z",
  "usage": { /* request quota usage */ }
}

Verify a token

POST /api/v1/verify

Server-to-server human verification. Submit a site_key and the token your form collected from the widget. The caller must either own the site (authenticated via API key) or present the site's secret. Returns the outcome band, score and reason.

Body parameters

ParamTypeDescription
site_keystring (required)Public site key for the protected form.
tokenstring (optional)The sentinel-token minted by the widget on a successful solve.
secretstring (optional)Site secret, for callers that don't own the key. Authorizes the verify.
telemetryobject (optional)Coarse, non-PII behavioral telemetry to feed bot scoring.

Example request

curl -X POST https://redeyed.com/api/v1/verify \
  -H "X-Api-Key: rdy_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "site_key": "site_xxxxxxxxxxxx",
    "token": "sentinel-token-from-widget"
  }'

Example response

{
  "success": true,
  "outcome": "passed",   // passed | challenged | blocked
  "score": 18,
  "reason": "Acceptable risk (score 18)"
}

Outcomes: passed — allow the request; challenged — re-prompt or step up; blocked — deny (high-risk IP or detected automation).

Widget embed

The Sentinel widget is a self-contained, vanilla-JS captcha — no build step. Load the script once, then drop a .sentinel-captcha element with your site key inside any form. The widget auto-mounts, runs the challenge, and injects a hidden sentinel-token input on success.

Embed

<!-- Load once, anywhere on the page -->
<script src="https://redeyed.com/sentinel.js" async></script>

<!-- Place inside the form you want to protect -->
<form method="POST" action="/signup">
  <!-- ...your fields... -->
  <div class="sentinel-captcha" data-sitekey="site_xxxxxxxxxxxx"></div>
  <button type="submit">Create account</button>
</form>

On success the widget appends <input type="hidden" name="sentinel-token"> to the enclosing form and fires a sentinel:solved event. Submit the token to your backend and confirm it with POST /api/v1/verify. You can also drive the widget programmatically with window.Sentinel.render() and window.Sentinel.reset().

Challenge / solve flow

The widget uses two same-origin endpoints under the hood. You normally never call these directly, but they're documented for custom integrations.

1. Issue a challenge

POST /sentinel/challenge

Issues an adaptive proof-of-work challenge for a site key. Difficulty scales with the IP's live risk band; high-risk IPs are blocked rather than challenged.

Request & response

curl -X POST https://redeyed.com/sentinel/challenge \
  -H "Content-Type: application/json" \
  -d '{ "site_key": "site_xxxxxxxxxxxx" }'

// 200 OK
{
  "challenge_id": "aZ8...40-char-token",
  "nonce": "16-char-nonce",
  "difficulty": 3
}
// 403 { "error": "Access denied.", "blocked": true } for high-risk IPs

2. Solve the challenge

POST /sentinel/solve

Find an integer solution so that sha256(nonce + solution) starts with difficulty leading hex zeros, then post it back. The server recomputes the hash, checks it's unsolved / unexpired / IP-matched, and mints a short-lived HMAC token.

Request & response

curl -X POST https://redeyed.com/sentinel/solve \
  -H "Content-Type: application/json" \
  -d '{
    "challenge_id": "aZ8...40-char-token",
    "solution": "104729",
    "telemetry": { "solve_ms": 380, "pointer_entropy": 0.42 }
  }'

// 200 OK
{
  "token": "signed-sentinel-token",
  "bot_likelihood": 7
}

Submit the returned token with your form, then confirm it server-side via POST /api/v1/verify.

Reputation response fields

Fields returned by GET /api/v1/ip/{ip}.

FieldTypeDescription
ipstringThe IP address that was scored.
scoreintegerReputation score, 0–100. Higher means riskier.
riskstringRisk band derived from the score: clean, low, medium or high.
flags.is_proxybooleanIP is a known proxy.
flags.is_vpnbooleanIP belongs to a VPN provider.
flags.is_torbooleanIP is a Tor exit / relay node.
flags.is_datacenterbooleanIP is hosting / datacenter, not residential.
flags.is_botbooleanIP is associated with automated / bot traffic.
flags.is_abuserbooleanIP appears on abuse / reputation blocklists.
country_codestring|nullISO 3166-1 alpha-2 country code (self-owned geo data).
asninteger|nullAutonomous System Number.
asn_orgstring|nullOrganization that owns the ASN.
connection_typestringe.g. residential, mobile, hosting, datacenter, education or unknown.
confidenceintegerConfidence in the score (rises with observed hits).
last_seen_atdatetime|nullWhen this IP was last observed.
Ready to integrate? Create your API keys and Sentinel sites in the Lab.