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

  1. 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-Id ag_a1b2c3d4
    X-VP-Session ses_e5f6a7b8
  2. You call the verify endpoint

    Send those values to the VerifiedProxy public API. No API key needed — it's a free, open endpoint.

  3. 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:

curl
# 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
curl "https://app.verifiedproxy.com/api/agents/verify?agent_id=ag_a1b2c3d4&session=ses_e5f6a7b8&url=https://yoursite.com"
⚠️
The URL you pass must exactly match the URL the agent's proxy used when it created the session. This is an exact string match (compared via SHA-256 hash), not a fuzzy domain check. Read on to understand what that means for your setup.

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 tobefore 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
ℹ️
Key takeaway: The session stores the domain the agent originally connected to — before any HTTP redirect. If your server redirects www.yoursite.comyoursite.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 url parameter. 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 session parameter 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

javascript
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' });
  }
});
python
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
<?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']);
}
go
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

pseudocode
if (request has VP headers AND verify() returns verified) {
    // Skip CAPTCHA for verified agents
    serve_content();
} else {
    show_captcha();
}

Scope-Based Access Control

pseudocode
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

pseudocode
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 url parameter 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.
The verification endpoint is free, public, and requires no API key. There's no cost or signup needed to verify agents on your website.