Inbound surfaces
Verify the AI agents that reach into the surfaces you operate, a website, an HTTP API, an MCP server, an A2A agent, or a webhook, with one verify endpoint and a per-kind challenge.
The rest of AxioRank governs the calls your agents make outbound. Inbound surfaces are the other direction: the AI agents reaching into something you operate. A surface is a typed entry point you register once, and every request to it is verified, scored, and allowed, challenged, or blocked, with the same audit trail as an outbound tool call.
One endpoint, five kinds
Every surface kind resolves through a single gateway endpoint,
POST /api/gateway/verify-request, authenticated with a surface site key
(axr_site_...), distinct from your agent key. The SDK helpers build the call
for you; you rarely hit the endpoint by hand.
The five surface kinds
| Kind | What it protects | How a caller proves identity |
|---|---|---|
website | A site or app AI agents browse | Web Bot Auth signature (RFC 9421), reverse-DNS forward-confirm, or a known user-agent |
http_api | A REST / RPC API | An OAuth/OIDC bearer token (JWT verified against the issuer's JWKS), an mTLS/SPIFFE id, or a static API key |
mcp_server | An MCP server you expose | The connecting agent's signed agent card (JWS, key-domain anchored) |
a2a_agent | An A2A agent you expose | The connecting agent's signed agent card (JWS, key-domain anchored) |
webhook | A webhook receiver | An HMAC signature scheme, or the same HTTP signals as a website |
Register a surface from Settings, Surfaces or with POST /api/surfaces. Each
surface counts against your plan's surface limit (1 on Free, 5 on Pro, 25 on
Team, unlimited on Enterprise); there is no per-kind gate, so every plan can use
every kind.
The verdict
A verified request resolves to one of three decisions, which already reflects the surface's posture:
allow: the caller is trusted enough for the operation. Serve the request.challenge: ask the caller to prove more (re-sign, step up, or authenticate).block: refuse the request.
The result also carries a verification block (status, method, and a
confidence score) and, for a non-allow verdict, a per-kind challenge
instruction so you enforce it in the caller's own protocol:
| Surface | Challenge transport | Maps to |
|---|---|---|
website, webhook | http | HTTP 401 (challenge) or 403 (block) |
http_api | http_stepup | 401 with a WWW-Authenticate: Bearer step-up (RFC 9470) |
mcp_server | jsonrpc | A JSON-RPC error, code -32001 (challenge) or -32002 (block) |
a2a_agent | a2a_task | A2A task state auth-required (challenge) or rejected (block) |
Confidence
confidence grades how strongly identity was proven, on a 0 to 100 scale:
- 100: cryptographic proof. A valid signature, a JWT that verifies against the JWKS, an mTLS certificate chain, or a signed card.
- 70: a reverse-DNS lookup that forward-confirms a known crawler.
- 40: a user-agent string that matches a known agent but is not proven.
- 0: spoofed or unknown.
Monitor first, then enforce
A surface starts in monitor posture: AxioRank still computes and logs the
verdict, but the response stays allow and enforced is false. Watch the
audit log to see what would have been challenged or
blocked, then switch to enforce when you are confident. Pair this with
inbound policies to scope decisions by surface kind and
operation (an MCP tool name, a JSON-RPC method, or an API operationId).
Fail-open by design
Inbound verification sits in the hot path of your own requests, so it fails open. Only a misconfigured site key raises; any timeout or transport failure resolves to a synthetic verdict (allow by default, configurable per call), so a verification outage never takes your surface down.
Verify from the SDK
A website or HTTP request
In TypeScript, verifyRequest takes a standard Request, so it drops into
Next.js middleware, Hono, or Workers; axioGuard wraps it as ready-made
middleware:
import { verifyRequest } from "@axiorank/sdk";
const result = await verifyRequest(req, { apiKey: process.env.AXIORANK_SITE_KEY! });
if (result.decision !== "allow") {
return new Response("Verification required", { status: 401 });
}In Python, verify_request takes the request metadata (the Web Bot Auth
signature headers are lifted out for you), with sync and async variants for
Flask or FastAPI:
from axiorank import verify_request
result = verify_request(
method=request.method,
authority=request.host,
path=request.path,
headers=dict(request.headers),
api_key=os.environ["AXIORANK_SITE_KEY"],
)
if result.decision != "allow":
abort(401)An MCP server, A2A agent, HTTP API, or webhook
For an agent-native surface there is no standard HTTP Request to hand over, so
you supply the caller's identity material directly. TypeScript:
import { verifySurface } from "@axiorank/sdk";
const result = await verifySurface(
{ surfaceKind: "mcp_server", operation: "tools/call", agentCard: incomingCard },
{ apiKey: process.env.AXIORANK_SITE_KEY! },
);
if (result.decision !== "allow") throw rpcError(result.challenge);Python (verify_surface, with a verify_surface_async for ASGI servers):
from axiorank import verify_surface
result = verify_surface(
{"surface_kind": "http_api", "auth_token": bearer_token},
api_key=os.environ["AXIORANK_SITE_KEY"],
)
if result.decision != "allow":
raise stepup_required(result.challenge)Next steps
- Policies: scope inbound decisions by surface kind and operation.
- Gateway API: the raw
verify-requestcontract. - Protocol adapters: the agent-card formats AxioRank verifies.
- Claims: publish your own signed identity for others to verify.