Website Integration
Add agent verification to your website so you know exactly which AI agent is visiting, what organisation sent it, and what it's authorised to do. It's free, public, and takes minutes to set up.
How Verification Works
-
Agent visits your site
When a verified agent visits your website, the VerifiedProxy proxy has already created a session for your domain on the agent's behalf. The request includes two identity headers:
Header Example X-VP-Agent-Idag_a1b2c3d4X-VP-Sessionses_e5f6a7b8 -
You call the verify endpoint
Send those values to the VerifiedProxy public API. No API key needed — it's a free, open endpoint.
-
You get back the agent's identity
The response tells you the agent's name, organisation, scopes, and whether the session is valid. You decide what to do with that info.
Quick Integration
The simplest integration is a single API call:
# Verify an agent (no auth required)
curl "https://app.verifiedproxy.com/api/agents/verify?agent_id=ag_a1b2c3d4&session=ses_e5f6a7b8"
Verifying the URL — How Domain Matching Works
You can optionally pass your website's URL in the url parameter to confirm that the session was actually created for your domain. This is recommended — it prevents someone from reusing a session that was created for a different site.
curl "https://app.verifiedproxy.com/api/agents/verify?agent_id=ag_a1b2c3d4&session=ses_e5f6a7b8&url=https://yoursite.com"
How the matching works under the hood
When the agent's proxy creates a session, it sends a target_url — for example https://yoursite.com. The server stores a SHA-256 hash of that exact URL string. When you verify with the url parameter, the server hashes what you send and compares the two hashes.
This means: the URL you verify with must be character-for-character identical to what the proxy sent. Here's what to keep in mind:
Redirects matter
The agent's proxy creates the session using the URL the agent is trying to connect to — before any redirects happen. For HTTPS connections, the proxy uses just the hostname (e.g. https://yoursite.com), so the session URL is typically the bare domain.
| Your setup | URL stored in the session | What you should verify with |
|---|---|---|
No redirects — site lives at yoursite.com |
https://yoursite.com |
https://yoursite.com |
www.yoursite.com redirects to yoursite.com |
https://www.yoursite.com (if the agent connects to www first) |
https://www.yoursite.com (the pre-redirect domain) |
yoursite.com redirects to www.yoursite.com |
https://yoursite.com (the pre-redirect domain) |
https://yoursite.com |
www.yoursite.com → yoursite.com, and the agent connects to www.yoursite.com, the session URL is https://www.yoursite.com. Your verify call should use that same URL. When in doubt, check which domain is in the agent's DNS — that's the one the proxy will use.
Best practices for URL verification
- Know your canonical domain. Check which domain browsers and agents actually connect to. If your DNS or CDN redirects
www.to the bare domain (or vice versa), the agent connects to whichever domain it initially resolves to. - When in doubt, skip the
urlparameter. Verification still works without it — you just don't get the domain-binding check. The agent ID and session ID are still validated. - The session ID is already strong proof. The
sessionparameter is a unique, time-limited token that the agent's proxy created specifically for this visit. Even without URL matching, it's reliable proof of identity. - Test it first. Create a test session targeting your domain, then try verifying with different URL variations to see what matches.
Common URL gotchas
| Situation | Result |
|---|---|
Session created for https://yoursite.com, you verify with https://yoursite.com/ (trailing slash) |
url_mismatch — trailing slashes change the hash |
Session created for https://yoursite.com, you verify with http://yoursite.com |
url_mismatch — different protocol |
Session created for https://YOURSITE.COM, you verify with https://yoursite.com |
url_mismatch — case matters in the hash |
Session created for https://yoursite.com, you skip the url parameter entirely |
verified: true — URL check is skipped, everything else still checked |
Code Examples
const express = require('express');
const app = express();
// Middleware to verify VerifiedProxy agents
async function verifyAgent(req, res, next) {
const agentId = req.headers['x-vp-agent-id'];
const session = req.headers['x-vp-session'];
if (!agentId || !session) {
// Not a verified agent request — handle normally
req.vpAgent = null;
return next();
}
try {
const url = `https://app.verifiedproxy.com/api/agents/verify`
+ `?agent_id=${agentId}&session=${session}`;
const resp = await fetch(url);
const data = await resp.json();
if (data.verified) {
req.vpAgent = data.agent;
req.vpSession = data.session;
} else {
req.vpAgent = null;
req.vpReason = data.reason;
}
} catch (err) {
req.vpAgent = null;
}
next();
}
app.use(verifyAgent);
app.get('/', (req, res) => {
if (req.vpAgent) {
res.json({
message: `Welcome, ${req.vpAgent.name} from ${req.vpAgent.org_name}`,
scopes: req.vpAgent.scopes,
});
} else {
res.json({ message: 'Hello, visitor' });
}
});
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
VP_VERIFY_URL = "https://app.verifiedproxy.com/api/agents/verify"
def verify_agent():
"""Check if the request comes from a verified agent."""
agent_id = request.headers.get("X-VP-Agent-Id")
session = request.headers.get("X-VP-Session")
if not agent_id or not session:
return None
try:
resp = requests.get(VP_VERIFY_URL, params={
"agent_id": agent_id,
"session": session,
})
data = resp.json()
if data.get("verified"):
return data["agent"]
except Exception:
pass
return None
@app.route("/")
def index():
agent = verify_agent()
if agent:
return jsonify({
"message": f"Welcome, {agent['name']} from {agent['org_name']}",
"scopes": agent["scopes"],
})
return jsonify({"message": "Hello, visitor"})
<?php
function verifyAgent(): ?array {
$agentId = $_SERVER['HTTP_X_VP_AGENT_ID'] ?? null;
$session = $_SERVER['HTTP_X_VP_SESSION'] ?? null;
if (!$agentId || !$session) {
return null;
}
$url = "https://app.verifiedproxy.com/api/agents/verify"
. "?agent_id=" . urlencode($agentId)
. "&session=" . urlencode($session);
$response = @file_get_contents($url);
if ($response === false) {
return null;
}
$data = json_decode($response, true);
if ($data && ($data['verified'] ?? false)) {
return $data['agent'];
}
return null;
}
// Usage
$agent = verifyAgent();
if ($agent) {
header('Content-Type: application/json');
echo json_encode([
'message' => "Welcome, {$agent['name']} from {$agent['org_name']}",
'scopes' => $agent['scopes'],
]);
} else {
echo json_encode(['message' => 'Hello, visitor']);
}
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
type VerifyResponse struct {
Verified bool `json:"verified"`
Agent struct {
ID string `json:"id"`
Name string `json:"name"`
OrgName string `json:"org_name"`
Scopes []string `json:"scopes"`
} `json:"agent"`
}
func verifyAgent(r *http.Request) *VerifyResponse {
agentID := r.Header.Get("X-VP-Agent-Id")
session := r.Header.Get("X-VP-Session")
if agentID == "" || session == "" {
return nil
}
verifyURL := fmt.Sprintf(
"https://app.verifiedproxy.com/api/agents/verify?agent_id=%s&session=%s",
url.QueryEscape(agentID),
url.QueryEscape(session),
)
resp, err := http.Get(verifyURL)
if err != nil {
return nil
}
defer resp.Body.Close()
var result VerifyResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil
}
if result.Verified {
return &result
}
return nil
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
result := verifyAgent(r)
if result != nil {
json.NewEncoder(w).Encode(map[string]interface{}{
"message": fmt.Sprintf("Welcome, %s from %s",
result.Agent.Name, result.Agent.OrgName),
"scopes": result.Agent.Scopes,
})
} else {
json.NewEncoder(w).Encode(map[string]string{
"message": "Hello, visitor",
})
}
}
Use Cases
Allow Verified Agents to Bypass CAPTCHAs
if (request has VP headers AND verify() returns verified) {
// Skip CAPTCHA for verified agents
serve_content();
} else {
show_captcha();
}
Scope-Based Access Control
agent = verify(agent_id, session);
if (agent.verified) {
if ("data-collect" in agent.scopes) {
// Allow API access for data collection
serve_api_data();
} else if ("browse" in agent.scopes) {
// Read-only page access
serve_read_only();
}
} else {
// Unverified — standard rate limits
apply_rate_limit();
}
Agent-Specific Rate Limits
agent = verify(agent_id, session);
if (agent.verified) {
// Higher rate limit for verified agents
rate_limit = 1000 requests/hour;
// Track per-org for analytics
track_usage(agent.org_name, request);
} else {
rate_limit = 100 requests/hour;
}
Best Practices
- Cache verification results for the session duration (up to 10 minutes) to reduce API calls.
- Use URL matching by passing your domain in the
urlparameter to ensure the session was created for your site. See the URL verification section above for details on redirects and domain matching. - Fail open if the VerifiedProxy API is unreachable. Treat the request as a normal (unverified) visitor rather than blocking it.
- Log agent activity for analytics. The agent ID and org name help you understand how AI agents use your site.
- Check scopes before allowing sensitive operations. Use scopes to decide what level of access a verified agent should get.