Query IP reputation, verify human-proof tokens, and embed the self-hosted captcha widget. Everything you need to integrate Sentinel.
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
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
}
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.
| Param | Type | Description |
|---|---|---|
ip | string (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 */ }
}
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.
| Param | Type | Description |
|---|---|---|
site_key | string (required) | Public site key for the protected form. |
token | string (optional) | The sentinel-token minted by the widget on a successful solve. |
secret | string (optional) | Site secret, for callers that don't own the key. Authorizes the verify. |
telemetry | object (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).
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().
The widget uses two same-origin endpoints under the hood. You normally never call these directly, but they're documented for custom integrations.
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
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.
Fields returned by GET /api/v1/ip/{ip}.
| Field | Type | Description |
|---|---|---|
ip | string | The IP address that was scored. |
score | integer | Reputation score, 0–100. Higher means riskier. |
risk | string | Risk band derived from the score: clean, low, medium or high. |
flags.is_proxy | boolean | IP is a known proxy. |
flags.is_vpn | boolean | IP belongs to a VPN provider. |
flags.is_tor | boolean | IP is a Tor exit / relay node. |
flags.is_datacenter | boolean | IP is hosting / datacenter, not residential. |
flags.is_bot | boolean | IP is associated with automated / bot traffic. |
flags.is_abuser | boolean | IP appears on abuse / reputation blocklists. |
country_code | string|null | ISO 3166-1 alpha-2 country code (self-owned geo data). |
asn | integer|null | Autonomous System Number. |
asn_org | string|null | Organization that owns the ASN. |
connection_type | string | e.g. residential, mobile, hosting, datacenter, education or unknown. |
confidence | integer | Confidence in the score (rises with observed hits). |
last_seen_at | datetime|null | When this IP was last observed. |