IP Decision Engine
The Decision Engine turns IPBot’s risk score and evidence into a usable, explainable decision: a factual role, a business profile, a recommended action, per-scenario decisions (login, signup, payment, API…), the guardrails that were applied, and a human-readable explanation.
It is fully additive. The existing public fields — score.risk_score,
score.verdict, and score.recommended_action — are never changed by the
Decision Engine. It only annotates the response with an actionable decision view
you can consume directly or ignore.
The four objects appear at the top level of every /v1/ip/* response and are
projectable via ?fields=decision,scores,scenarios,explanation.
decision
Section titled “decision”The headline recommendation.
{ "decision": { "profile": "trusted_infrastructure", "role": "public_dns_resolver", "action": "allow", "risk_level": "low", "confidence": "high", "policy_version": "decision-v1-2026-06.2", "allowed_actions": ["allow", "monitor"], "blocked_actions": ["block"], "guardrails_applied": ["public_dns_resolver_cannot_block"] }}| Field | Meaning |
|---|---|
role | Best factual classification of the IP (e.g. verified_crawler, tor_exit, datacenter, residential_proxy). |
profile | Business-facing interpretation of the role (e.g. trusted_infrastructure, anonymizing_network, ordinary_datacenter). |
action | Recommended, guardrail-constrained action: allow, monitor, rate_limit, challenge, manual_review, block. |
risk_level | low / medium / high. |
confidence | Calculated confidence in this decision (low / medium / high), combining evidence quality, how decisively the score clears an action boundary, source authority, any trust-vs-adverse-risk contradiction, and guardrail stability. Infrastructure context is not treated as adverse by itself. |
policy_version | The decision policy that produced this result. Also surfaced at GET /health as build.decision_policy_version. |
allowed_actions / blocked_actions | The action envelope for this role (e.g. a verified crawler can never be blocked). |
guardrails_applied | Safety rules that constrained the action (see below). |
scores
Section titled “scores”The expanded, bounded (0–100) score family. risk_score mirrors the public
score.risk_score; the sub-scores break the basis down by group.
{ "scores": { "risk_score": 30, "base_risk_score": 30, "abuse_score": 0, "anonymity_score": 0, "trust_score": 90, "infrastructure_score": 0, "routing_risk_score": 0, "evidence_quality_score": 95 }}trust_score reflects official/verified signals (verified crawler, public DNS,
special-use); abuse_score reflects threat-list evidence; anonymity_score
reflects Tor/VPN/proxy; infrastructure_score is identity/context (cloud, CDN,
datacenter) — not automatically abuse; evidence_quality_score reflects how
authoritative the evidence basis is.
scenarios
Section titled “scenarios”The same IP carries different risk depending on what it is doing. Each scenario picks its own action by expected loss — the action with the lowest combined cost of being wrong, given the IP’s evidence and that scenario’s stakes — so the friction is proportionate per surface. A clean IP gets no friction anywhere; friction rises as abuse likelihood and the scenario’s stakes (signup/payment weigh false negatives more than content) increase. The example below is a VPN:
{ "scenarios": { "content": { "action": "monitor", "risk_level": "medium", "confidence": "medium", "reason": "VPN traffic is allowed but should be monitored in this scenario." }, "seo_crawler": { "action": "monitor", "risk_level": "medium", "confidence": "medium", "reason": "VPN traffic is allowed but should be monitored in this scenario." }, "login": { "action": "rate_limit", "risk_level": "medium", "confidence": "medium", "reason": "VPN traffic should be rate limited in this scenario." }, "signup": { "action": "challenge", "risk_level": "medium", "confidence": "high", "reason": "VPN traffic should face additional friction in this scenario." }, "payment": { "action": "rate_limit", "risk_level": "medium", "confidence": "medium", "reason": "VPN traffic should be rate limited in this scenario." }, "api": { "action": "rate_limit", "risk_level": "medium", "confidence": "medium", "reason": "VPN traffic should be rate limited in this scenario." } }}Per-scenario actions then pass through the same guardrail layer as the
top-level decision, so they stay consistent with the role’s allowed_actions (a
verified crawler is never challenged in signup; a known abuser is never
plain-allowed in content; a Tor exit floors to at least challenge).
scenarios.*.confidence is computed per scenario from that scenario’s own
expected-loss margin (how decisively the chosen action beat the runner-up),
combined with the shared evidence-quality / source-authority / contradiction /
guardrail components — so a clear-cut scenario reads more confident than a close
call. The full component breakdown for the top-level confidence is available only
in the admin scoring trace at GET /v1/internal/score/{ip} as
trace.decision_confidence.
explanation
Section titled “explanation”A deterministic, auditable account of how the decision was reached.
{ "explanation": { "summary": "datacenter classified as ordinary_datacenter", "key_reason": "datacenter", "drivers": [ { "type": "infrastructure", "label": "infra_network", "impact": "high", "impact_score": 80, "direction": "raises_risk", "reason": "infra_network evidence (registry)" } ], "guardrails_applied": [], "reason_chain": [ "datacenter classified as ordinary_datacenter", "decision action constrained to allow" ] }}Guardrails
Section titled “Guardrails”Guardrails are deterministic safety rules. Floors raise a too-lenient action to a minimum; caps lower a too-strict action for protected roles. Caps run last, so they are authoritative.
| Guardrail | Effect |
|---|---|
verified_crawler_cannot_block | Verified crawlers are capped at monitor (never challenged/blocked). |
public_dns_resolver_cannot_block | Public DNS resolvers are capped at monitor. |
special_use_must_allow | Special-use / documentation / private ranges always allow. |
routing_conflict_requires_manual_review | RPKI/origin conflict raises non-block actions to manual_review. |
strong_threat_floor_cannot_allow | A known abuser can never stay allow (raised to at least monitor). |
tor_exit_floor | Tor exits get at least challenge where they would otherwise allow. |
proxy_floor | VPN / residential / public proxies get at least monitor where they would otherwise allow. |
Using it
Section titled “Using it”Request just the decision surface with field projection:
curl -s "https://api.ipbot.com/v1/ip/8.8.8.8?fields=decision,scenarios"Then branch on the scenario relevant to the request you’re handling — e.g. use
scenarios.payment.action at checkout and scenarios.login.action at sign-in —
or use the top-level decision.action for a single global policy.
The decision layer never changes score.risk_score / verdict /
recommended_action, so you can adopt it incrementally alongside your existing
score-based logic.