BotGuard
Production API · OpenAPI 3.0.3

Connection risk you can inspect, explain, and enforce.

BotGuard returns request-scoped bot, automation, proxy, VPN, protocol, and browser-consistency evidence with stable machine-readable detection codes. It exposes both a compact /me verdict and a privacy-oriented v21 fingerprint observation.

Quick start

A GET scores only what the service can observe from the connection and request. A POST adds optional browser telemetry while the server remains the sole scorer.

curl --silent --show-error https://bot.pwn-all.com/me

curl --silent --show-error https://bot.pwn-all.com/me \
  --header 'content-type: application/json' \
  --data '{"js":{"webdriver":false,"languages":["en-US"]},"locale":{"timezone":"Europe/London"}}'

Selected fields from a verdict:

{
  "policy_version": "2026-07-12.1",
  "score": 36,
  "action": "observe",
  "signals": [
    {
      "code": "network.geo_rtt",
      "category": "network",
      "message": "RTT below geo floor (proxy/VPN near server)"
    }
  ],
  "reasons": ["RTT below geo floor (proxy/VPN near server)"]
}
Do not cache API JSON. Every route listed below carries Cache-Control: no-store, max-age=0. Detection results belong to the current connection, while status, service, and contact data should be revalidated when requested.

Public client routes

Route What it returns Cache behavior
GET/api/status Process version, server timestamp, and discovery links. no-store, max-age=0
GET/me 1–100 server-observed connection verdict, scoring policy_version, and structured signals[]; client_seen=false. no-store, max-age=0
POST/me The same verdict after an optional valid browser-signal merge. Invalid or unreadable JSON returns a server-only verdict with client_seen=false. no-store, max-age=0
GET/api/fp/server-observed Connection, TCP/TLS, protocol, coarse network, and request-header observations without running the v21 scorer. no-store, max-age=0
POST/api/fp/observation Normalized v21 HMAC-grouped fingerprints, risk dimensions, confidence, temporal drift, action, and structured risk.signals[]. no-store, max-age=0
GET/api/agent/services Read-only catalogue of public engineering, security, recovery, and protection services. no-store, max-age=0
GET/api/agent/contact-options Public email, PGP-key, and Signal links. Reading it sends nothing. no-store, max-age=0

The complete field schemas, nullability, and response variants are in the OpenAPI document. Unlisted administrative and compatibility handlers are not part of this public contract.

Choose the contract that matches your integration

/me: compact enforcement verdict

Use this for a direct 1–100 answer, scoring-policy version, structured detections, legacy human-readable reasons, and the underlying server signal object. The initial GET skips delayed HTTP/3 and honeypot-completion checks; the subsequent POST activates them and reports whether valid browser telemetry was merged.

The nominal action bands are clean, observe, challenge, and block. A block safety gate requires corroboration across independent modalities unless a decisive signature is present.

/api/fp/observation: normalized v21 evidence

Use this when you need separate network, browser, protocol, automation, contradiction, and temporal dimensions. Fingerprint groups have domain-separated HMAC identifiers and structured components; risk.signals[] uses the same stable detection object as /me.

The v21 actions are allow, allow_log, challenge, and review_block. They are a different vocabulary from /me; do not map them by string position.

curl --silent --show-error https://bot.pwn-all.com/api/fp/observation \
  --header 'content-type: application/json' \
  --data '{
    "consent":{"security_purpose":true,"analytics_consent":false,"tracking_consent":false},
    "platform":"linux",
    "timezone":"Europe/London",
    "capabilities":"111",
    "webdriver":false,
    "automation":false
  }'

Stable detection metadata

Integrations should branch on action, signals[].code, and signals[].category. English reasons[] remains for backward compatibility, and each signal repeats that text in message, but wording can evolve without changing the underlying detection.

FieldContractUse
policy_versionStable rule-set identifier on /me.Store beside the decision for rollout and audit comparisons.
signals[].codeStable namespaced identifier such as automation.webdriver, network.geo_rtt, or protocol.http3.Feature flags, policy rules, metrics, and analytics dimensions.
signals[].categoryCoarse family: automation, browser, network, protocol, behavior, none, or other.Category-level policy and dashboards.
signals[].messageHuman-readable explanation.UI and diagnostics only; do not parse it.
reasons[]Legacy English messages in the same order as signals[].Backward-compatible display only.
Forward compatibility. Ignore unknown codes or categories unless your policy explicitly handles them. Do not turn a newly introduced signal into a block merely because it is unfamiliar.

Action semantics

/me nominal bands
ScoreActionSuggested handling
1–25cleanContinue normal flow.
26–50observeContinue and retain relevant risk metadata in your own policy logs.
51–75challengeApply a proportional verification step.
76–100block*Deny or isolate only under your policy.
v21 fixed bands
ScoreActionSuggested handling
0–29allowContinue normal flow.
30–59allow_logContinue with policy-level observation.
60–79challengeRequest additional proof.
80–100review_blockReview or block under your policy.

* A nominal /me block is downgraded to challenge when the score lacks two independent signal modalities and no decisive signature is present. All actions are advisory; this API does not change access in your application.

Limits and transport behavior

RequestPortable limitWhat happens above it
POST /me 65,536 bytes HTTP/3 returns 413. On HTTP/1.1 or HTTP/2, a handler read-limit failure is treated as no client observation and still produces a server-derived verdict with client_seen=false.
POST /api/fp/observation 65,536 bytes across all transports HTTP/3 returns 413 above 65,536 bytes. HTTP/1.1 and HTTP/2 accept up to 131,072 bytes and return 413 above that cap. Invalid JSON returns 400.
Public HTTP/1.1 and HTTP/2 requests 30-second processing timeout The server can return 408 when the request does not complete in time.

No per-client request-rate quota is published. Poll conservatively and back off on timeouts or server errors.

Fail-open is signal-local

Missing capture means unknown

If passive TCP, TLS/JA4, ASN, geo, DNS, HTTP/3, or browser telemetry is unavailable, rules that depend on it stay dormant. Null, false, empty, or an absent reason does not prove a human client.

Invalid input has route-specific handling

POST /me treats malformed or empty JSON as an absent client observation and returns a server-derived 200 verdict; bodies above 64 KiB return 413 on every transport. POST /api/fp/observation requires valid JSON and returns 400 when parsing fails. Valid partial objects use neutral defaults for omitted fields.

Kill switch is a 200 variant

Fingerprint routes can return {"telemetry_disabled":true}. Branch on that property before reading server_observed, risk, or group hashes.

Other evidence still counts

Fail-open does not force the final action to allow. Available, corroborated signals can still produce an observe, challenge, or block recommendation.

Integration checklist

  1. Treat each verdict as belonging to the current request path and connection, not as a permanent identity label.
  2. Branch on the documented action vocabulary and stable signal codes/categories; keep policy version, score, confidence, messages, and protocol for auditability.
  3. Use a proportional challenge before irreversible action, especially when confidence is low or important captures are absent.
  4. Honor no-store on every documented JSON response, especially in shared reverse proxies.
  5. Keep request payloads below 64 KiB and tolerate new response properties for forward compatibility.
Privacy note. Direct connection responses can include the calling IP address and transport fingerprints. The normalized v21 observation uses a coarse /24 IPv4 or /48 IPv6 prefix plus HMAC-grouped identifiers, but it is still security telemetry and should be handled accordingly.

Machine discovery