Observed since 21 Oct, 08:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
FastAPI middleware for AgentVisa AI agent verification
FastAPI middleware for AgentVisa — verify that incoming AI agents were authorized by a real, biometrically authenticated human before granting access.
pip install agentvisa-fastapi
from fastapi import FastAPI, Depends
from agentvisa_fastapi import AgentVisaConfig, AgentVisaMiddleware, require_agentvisa
app = FastAPI()
config = AgentVisaConfig(
api_key=os.environ["AGENTVISA_API_KEY"], # your Widget Holder API key — server-side only
widget_id=os.environ["AGENTVISA_WIDGET_ID"], # from your AgentVisa dashboard
)
# Add middleware — enriches every request with request.state.agentvisa
app.add_middleware(AgentVisaMiddleware, config=config)
# Protected route — agents without a valid AgentVisa token get 401
@app.get("/api/orders")
async def get_orders(av=Depends(require_agentvisa(config))):
return {"orders": [...], "plan": av.plan}
# Unprotected route — check manually if you want soft enforcement
@app.get("/api/public")
async def public(request: Request):
av = request.state.agentvisa
if av.valid:
return {"message": "Hello, verified agent!", "plan": av.plan}
return {"message": "Hello — consider getting an AgentVisa for full access."}
When an AI agent hits your site, AgentVisa requires a two-step handshake:
X-AgentVisa-Required: your_widget_id — this middleware does that automatically via require_agentvisa.X-AgentVisa-Token: tmp_... in the header./v1/verify with your API key and returns the result.The human behind the agent completed 5-factor verification (email, phone, cross-verification, biometric authentication, and a human assertion) when they got their AgentVisa.
AgentVisaConfig(
api_key="wk_...", # required — your Widget Holder API key
widget_id="wgt_...", # required — your widget ID
api_base="https://api.agentvisa.ai", # optional — override for self-hosted
timeout=5.0, # optional — /v1/verify timeout in seconds
)
Security note: Never expose api_key in client-side code, environment variables that get bundled, or logs. Treat it like a database password.
| Field | Type | Description |
|---|---|---|
valid | bool | Whether the agent is verified |
reason | str | "ok", "invalid", "expired", "revoked", "reverification_required", "token_not_present" |
plan | str | "basic" or "pro" |
widget_id | str | Your widget ID |
verified_at | datetime | None | When the token was issued |
expires_at | datetime | None | When the token expires |
domain_verified | bool | None | Whether the widget's registered domain is verified |
age_over_18 | "y" | "n" | "null" | None | Pro plan only — AVS-style flag, no raw DOB |
age_over_21 | "y" | "n" | "null" | None | Pro plan only — AVS-style flag |
multiple_agents_authorized | "y" | "n" | None | Pro plan only |
web_bot_auth_bound | bool | None | Pro plan — token covered by RFC 9421 Signature-Input |
skipped | bool | True when no token was in the request |
If you don't want to add middleware, use the dependency standalone — it will call /v1/verify directly:
from agentvisa_fastapi import AgentVisaConfig, require_agentvisa
config = AgentVisaConfig(api_key="...", widget_id="...")
@app.post("/checkout")
async def checkout(av=Depends(require_agentvisa(config))):
# verified agents only
...
from agentvisa_fastapi import AgentVisaConfig, get_agentvisa, AgentVisaResult
config = AgentVisaConfig(api_key="...", widget_id="...")
@app.get("/search")
async def search(av: AgentVisaResult = Depends(get_agentvisa(config))):
results = perform_search()
if not av.valid:
# Return limited results for unverified agents
return {"results": results[:3], "note": "Get an AgentVisa for full results"}
return {"results": results}
When an agent's daily verification limit is hit, /v1/verify returns reason: "reverification_required". This middleware propagates that reason in the 401 response so your users know to check their email.
The require_agentvisa dependency returns:
HTTP 401
X-AgentVisa-Required: your_widget_id
{"detail": "AgentVisa reverification required — check email"}
The AgentVisa MCP server handles this automatically — it calls POST /v1/holder/reverify to trigger the re-verification email without the human needing to intervene.
MIT
pypistats · 49 observations shown. Missing days are unknown. Source category: overall: without mirrors.
| Date / source period | Value | Observed at |
|---|---|---|
| 2026-10-20 – 2026-10-20 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-18 – 2026-10-18 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-17 – 2026-10-17 | 6 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-16 – 2026-10-16 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-15 – 2026-10-15 | 4 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-14 – 2026-10-14 | 3 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-13 – 2026-10-13 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-12 – 2026-10-12 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-10 – 2026-10-10 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-09 – 2026-10-09 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-06 – 2026-10-06 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-02 – 2026-10-02 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-10-01 – 2026-10-01 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-30 – 2026-09-30 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-29 – 2026-09-29 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-27 – 2026-09-27 | 3 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-26 – 2026-09-26 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-23 – 2026-09-23 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-22 – 2026-09-22 | 3 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-21 – 2026-09-21 | 6 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-19 – 2026-09-19 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-16 – 2026-09-16 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-14 – 2026-09-14 | 4 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-13 – 2026-09-13 | 4 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-10 – 2026-09-10 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-07 – 2026-09-07 | 4 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-06 – 2026-09-06 | 5 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-03 – 2026-09-03 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-09-01 – 2026-09-01 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-31 – 2026-08-31 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-28 – 2026-08-28 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-25 – 2026-08-25 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-24 – 2026-08-24 | 5 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-23 – 2026-08-23 | 5 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-21 – 2026-08-21 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-17 – 2026-08-17 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-15 – 2026-08-15 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-14 – 2026-08-14 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-13 – 2026-08-13 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-09 – 2026-08-09 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-04 – 2026-08-04 | 3 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-03 – 2026-08-03 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-08-02 – 2026-08-02 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-31 – 2026-07-31 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-27 – 2026-07-27 | 3 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-26 – 2026-07-26 | 1 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-25 – 2026-07-25 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-24 – 2026-07-24 | 2 | 2026-10-21T12:22:36.4012461Z |
| 2026-07-23 – 2026-07-23 | 4 | 2026-10-21T12:22:36.4012461Z |
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.1 | 22 Jun 2026 | 5 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 21 Oct, 08:55 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:2939219f1c1c8afdc47242cae6973e3a66b8e77474f2a80e300c5174c4f9c86c