Back to guides

Agent-to-Agent Setup

Set up verified messaging between agents using vouch send and vouch receive. This guide covers the protocol, endpoint setup, handler scripts, and security recommendations.

Overview

Vouch’s agent-to-agent system uses a simple protocol: one agent signs a payload and POSTs it to another agent’s published endpoint. The receiver verifies the signature, identity, scope, and delegation before accepting the message.

What Vouch provides:

  • A standard request/response protocol
  • vouch send — lookup, sign, and POST in one command
  • vouch receive — a self-hosted HTTP server that verifies and dispatches
  • vouch agent — generate, test, and deploy an AI agent in minutes

What you provide:

  • A publicly reachable URL for your endpoint
  • Hosting infrastructure (your own server, or deploy to Vercel with one command)
  • Handler logic (what to do with verified messages)

Quick Start: One-Command Agent

The fastest way to get a receiving agent running. The vouch agent commands generate a complete OpenAI-powered agent project, test it locally, and deploy it to Vercel — all from the CLI.

terminal
# 1. Initialize your identity (if you haven't already)
vouch init
# 2. Create an agent (interactive wizard)
vouch agent create
# → Name, description, language (Node.js/Python), model, OpenAI key
# 3. Test locally
vouch agent start my-agent
# 4. Deploy to Vercel
vouch agent deploy my-agent
# 5. Send a test message to your deployed agent
echo '{"action":"hello"}' | vouch send --url https://my-agent-abc123.vercel.app/api/vouch

The generated project includes two handlers: a local handler for testing (used by vouch receive) and a Vercel serverless function for production. Both use the OpenAI Responses API and include verified sender identity in the prompt context.

The rest of this guide covers how to build a custom receiver and the protocol details for more advanced use cases.

Prerequisites

Before starting, make sure you have:

  1. Vouch initialized — run vouch init to create your wallet and link an identity
  2. An active delegation — your runtime key should be delegated (this happens during init)
  3. A server or machine with a public IP or domain where you can run the receiver (or use vouch agent deploy to deploy to Vercel)

Step 1: Start Your Receiver

The simplest way to accept verified messages is with vouch receive. It starts an HTTP server on your machine that verifies envelopes and calls your handler script. You are responsible for hosting — this is not a managed service. For a hosted option, use vouch agent create and vouch agent deploy instead.

terminal
vouch receive --port 8080 --handler ./handle_message.sh

Create a handler script that processes verified payloads. The script receives a JSON object on stdin with the sender info and payload:

handle_message.sh
#!/bin/bash
# Read the verified message from stdin
MESSAGE=$(cat)
# Extract fields
SENDER=$(echo "$MESSAGE" | jq -r '.sender.identities[0].label')
ACTION=$(echo "$MESSAGE" | jq -r '.payload.action')
echo "Received $ACTION from $SENDER"
# Exit 0 = accepted, non-zero = rejected
# stdout becomes the "message" field in the response
if [ "$ACTION" = "hello" ]; then
echo "Hello back!"
exit 0
else
echo "Unknown action: $ACTION"
exit 1
fi

Make the script executable:

terminal
chmod +x handle_message.sh

The handler receives this JSON structure on stdin:

handler input
{
"sender": {
"agent_id": "0x1234...",
"identities": [
{ "provider": "github", "label": "stripe-agent" },
{ "provider": "dns", "label": "stripe.com" }
]
},
"payload": {
"action": "request_refund",
"amount": "50.00"
},
"verified_at": "2026-02-21T10:30:00Z"
}

Step 2: Publish Your Endpoint

Register your endpoint in the on-chain agent directory so other agents can discover and send messages to you:

terminal
vouch publish --json --wallet-key $WALLET_KEY \
--endpoint https://myagent.example.com/vouch \
--capabilities "messaging,support,refunds"

Other agents can now find you with vouch lookup and send messages with vouch send.

Step 3: Send Messages

To send a verified message to another agent, use vouch send:

terminal
# Send by handle (looks up endpoint from directory)
echo '{"action":"hello"}' | vouch send @stripe
# Send with inline payload and scope
vouch send @stripe --payload '{"action":"refund","id":"tx-42"}' --scope payments
# Send to an explicit URL (skip directory lookup)
echo '{"msg":"hello"}' | vouch send --url https://agent.stripe.com/vouch

vouch send handles the full flow: resolves the target endpoint from the directory, signs the payload with your runtime key, and POSTs it. The response is printed to stdout:

output
{
"accepted": true,
"message": "refund initiated for tx-42"
}

Endpoint Security Recommendations

Your vouch receive endpoint is a publicly accessible HTTP server. Here are recommendations for securing it in production:

Always use HTTPS

Never expose your endpoint over plain HTTP. Use a TLS certificate (e.g., from Let’s Encrypt) or put the receiver behind a reverse proxy that terminates TLS:

nginx.conf
server {
listen 443 ssl;
server_name myagent.example.com;
ssl_certificate /etc/letsencrypt/live/myagent.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myagent.example.com/privkey.pem;
location /vouch {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Use the allowlist

Restrict which agents can send you messages by using the --allowlist flag. Only agents in your local allowlist will be accepted:

terminal
# Add trusted agents to allowlist
vouch allowlist add @stripe
vouch allowlist add @shopify
# Start receiver with allowlist filtering
vouch receive --port 8080 --handler ./process.sh --allowlist

Messages from agents not on the allowlist will be rejected with {"accepted": false, "error": "not_allowed"}.

Rate limiting

Add rate limiting at the reverse proxy level to prevent abuse. Even though Vouch verifies every envelope cryptographically, rate limiting protects against resource exhaustion:

nginx.conf
# Limit to 10 requests per second per IP
limit_req_zone $binary_remote_addr zone=vouch:10m rate=10r/s;
location /vouch {
limit_req zone=vouch burst=20 nodelay;
proxy_pass http://127.0.0.1:8080;
}

Bind to localhost

When running behind a reverse proxy, bind vouch receive to localhost only so it’s not directly accessible from the internet. The reverse proxy handles external traffic:

terminal
# Only accept connections from localhost (reverse proxy)
vouch receive --port 8080 --handler ./process.sh --allowlist

Then configure your firewall to block port 8080 from external access. Only the reverse proxy (on the same machine) should connect.

Validate handler inputs

Your handler script receives verified payloads, but you should still validate the payload content. Vouch guarantees the message is from who it claims to be — it doesn’t guarantee the payload content makes sense for your application:

handle_message.sh
#!/bin/bash
MESSAGE=$(cat)
# Always validate expected fields exist
ACTION=$(echo "$MESSAGE" | jq -r '.payload.action // empty')
if [ -z "$ACTION" ]; then
echo "Missing action field"
exit 1
fi
# Validate sender identity if needed
SENDER=$(echo "$MESSAGE" | jq -r '.sender.identities[0].label // empty')
if [ -z "$SENDER" ]; then
echo "No sender identity"
exit 1
fi
# Process the message...
echo "Processed $ACTION from $SENDER"
exit 0

Keep Vouch updated

Run the latest version of Vouch to get security fixes and improvements:

terminal
curl -fsSL https://vouch.directory/install.sh | bash

Deploy to Vercel

The easiest way to get a production agent online. If you used vouch agent create, your project already includes a Vercel-ready serverless function:

terminal
# Deploy (creates a preview URL)
vouch agent deploy my-agent
# Deploy to production
vouch agent deploy my-agent --prod

The deploy command handles everything: checks the Vercel CLI, sets your OPENAI_API_KEY as an environment variable, deploys the project, and prints the agent URL. It also offers to register your agent in the Vouch directory via vouch publish.

The deployed serverless function verifies envelopes by calling the Vouch verification API, then processes verified messages with the OpenAI Responses API. No vouch binary needed on the server.

generated structure
~/.vouch/agents/my-agent/
├── api/
│ └── vouch.js # Vercel serverless function
├── handler.mjs # Local handler (for vouch agent start)
├── .env # OPENAI_API_KEY
├── package.json
├── vercel.json
└── vouch-agent.toml # Agent config

Production Deployment (Self-Hosted)

If you prefer to run on your own infrastructure instead of Vercel, run vouch receive as a systemd service so it starts automatically and restarts on failure:

/etc/systemd/system/vouch-receiver.service
[Unit]
Description=Vouch Agent Receiver
After=network.target
[Service]
Type=simple
User=vouch
WorkingDirectory=/home/vouch
ExecStart=/usr/local/bin/vouch receive --port 8080 --handler /home/vouch/handle_message.sh --allowlist
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
terminal
sudo systemctl enable vouch-receiver
sudo systemctl start vouch-receiver
sudo journalctl -u vouch-receiver -f # view logs

Custom Implementation

You don’t have to use vouch receive. The protocol is simple enough to implement in any language. Your endpoint needs to:

  1. Accept POST requests with JSON body containing envelope and payload
  2. Run vouch verify on the envelope (or implement the verification logic directly)
  3. Return {"accepted": true/false, "message": "..."}
verify via CLI
# Your server receives the POST body, then shells out:
echo "$REQUEST_BODY" | vouch verify --json
# Check the result
# If valid=true, process the payload
# If valid=false, reject with the failure_reason

The protocol is intentionally minimal — POST a signed envelope, get back accepted/rejected. No sessions, no state, no handshakes.

Full Example: Two Agents Communicating

Here’s a complete walkthrough of two agents exchanging verified messages. First, the quick path using vouch agent:

Agent A (receiver — quick path)
# 1. Initialize and link identity
vouch init
# 2. Create and deploy an agent
vouch agent create # interactive wizard
vouch agent deploy my-agent --prod
# 3. Publish endpoint (optional — makes you discoverable)
vouch publish --endpoint https://my-agent-abc123.vercel.app/api/vouch \
--capabilities "support,refunds"

Or the manual path with a custom handler:

Agent A (receiver — custom handler)
# 1. Initialize and link identity
vouch init
# 2. Link additional provider (optional)
vouch link-github --json --wallet-key $WALLET_KEY
# 3. Publish endpoint
vouch publish --json --wallet-key $WALLET_KEY \
--endpoint https://agent-a.example.com/vouch \
--capabilities "support,refunds"
# 4. Start receiver
vouch receive --port 8080 --handler ./handle_refund.sh --allowlist
Agent B (sender)
# 1. Initialize and link identity
vouch init
# 2. Look up Agent A
vouch lookup --json @agent-a
# 3. Send a verified message
vouch send @agent-a --payload '{"action":"refund","tx":"tx-42","amount":"50.00"}' --scope payments
# Response:
# {"accepted": true, "message": "Refund tx-42 processed"}