A practical guide for implementing the AI Agent Identification Protocol.
Add identity to your requests:
headers = {
"AI-Agent": "OpenAI/GPT-5.2 (Atlas; 1.0) [aid=myapp-001]"
}That's it for minimal compliance. Add capabilities when appropriate.
OpenAI/GPT-5.2
Anthropic/claude-4.5-opus
Google/gemini-3-pro-preview
Meta/llama-3-70b
YourCompany/custom-model
If model unknown:
Vendor/unknown
(MyApp; 1.0.0)
(LangChain; 0.2.0)
(InternalBot; 2025.01)
(Tool; unknown) # Version not available
(SecretApp; redacted) # Intentionally withheld
Combined:
OpenAI/GPT-5.2 (MyApp; 1.0.0)
Stable identifier for analytics and allowlists:
[aid=myapp-prod-001]
[aid=a1b2c3d4e5f6]
Combined:
OpenAI/GPT-5.2 (MyApp; 1.0.0) [aid=myapp-prod-001]
Use coarse levels by default:
| Level | Meaning |
|---|---|
cap:readonly |
Read-only, no side effects |
cap:browse |
Web browsing, API calls |
cap:execute |
Code execution, file modification |
AI-Agent-Capabilities: cap:browse
Detailed (when safe):
AI-Agent-Capabilities: cap:execute;tools:bash,python;mcp:github
import requests
headers = {
"AI-Agent": "OpenAI/GPT-5.2 (MyBot; 1.0) [aid=mybot-001]",
"AI-Agent-Capabilities": "cap:browse"
}
response = requests.get("https://api.example.com/data", headers=headers)import httpx
headers = {
"AI-Agent": "Anthropic/claude-4.5 (AsyncBot; 2.0) [aid=async-001]",
"AI-Agent-Capabilities": "cap:execute;tools:web_search"
}
async with httpx.AsyncClient(headers=headers) as client:
response = await client.get("https://example.com")const headers = {
"AI-Agent": "OpenAI/GPT-5.2 (WebApp; 1.0) [aid=webapp-001]",
"AI-Agent-Capabilities": "cap:readonly"
};
const response = await fetch("https://example.com/api", { headers });import grpc
metadata = [
('x-ai-agent', 'OpenAI/GPT-5.2 (MyApp; 1.0) [aid=grpc-001]'),
('x-ai-agent-capabilities', 'cap:execute'),
]
response = stub.MyMethod(request, metadata=metadata)curl -H "AI-Agent: OpenAI/GPT-5.2 (CLI; 1.0) [aid=cli-001]" \
-H "AI-Agent-Capabilities: cap:browse" \
https://example.com/apiNote: Not all AI agents make network requests. AAIP applies to agents that interact with external services, regardless of autonomy level.
| Scenario | Use AAIP? |
|---|---|
| Browsing websites | ✅ Yes |
| Calling external APIs | ✅ Yes |
| Using MCP servers | ✅ Yes |
| gRPC service calls | ✅ Yes |
| Internal function calls only | ⚪ Optional |
| User-initiated browser requests | ❌ No (that's User-Agent) |
| Local-only operations | ❌ N/A |
General rule: If your AI agent interacts with external services—whether autonomous, semi-autonomous, or human-directed—include identity.
Browser products (or browser-embedded agents) that can autonomously navigate and act on pages—not just fetch URLs—require special consideration for AAIP.
The browser will still send a conventional User-Agent string identifying the browser engine. AAIP is the agent identity and SHOULD be sent as additional headers on every navigation request.
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0
AI-Agent: Perplexity/sonar-2 (Comet; 1.0) [aid=comet-prod]| Model | Description | Examples |
|---|---|---|
| Embedded Agent Browser | Browser product itself is agentic | Perplexity Comet, Opera Browser Operator |
| Remote Agent + Browser | Agent uses "its own browser" for tasks | OpenAI Operator |
| Agentic Feature in Browser | Mainstream browser with AI feature | Arc Max, Opera AI features |
| Field | How to Populate |
|---|---|
vendor/model |
The agent decision engine when known; use unknown if not introspectable |
(<application>; <version>) |
The browser product (Comet, Operator) or controlling agent app |
aid= |
Identifies the agent instance/deployment, NOT the end user |
The browser product itself is agentic. The "application" is the browser.
Example: Perplexity Comet
# Model known
AI-Agent: Perplexity/sonar-2 (Comet; 1.0) [aid=comet-prod]
AI-Agent-Capabilities: cap:browse
# Model not reliably knowable
AI-Agent: Perplexity/unknown (Comet; 1.0) [aid=comet-prod]
AI-Agent-Capabilities: cap:browse
An agent that explicitly uses its own browser instance.
Example: OpenAI Operator
AI-Agent: OpenAI/GPT-5.2 (Operator; 2026.01) [aid=operator-prod]
AI-Agent-Capabilities: cap:browse
A mainstream browser with an agentic AI feature.
Example: Opera Browser Operator
AI-Agent: Opera/unknown (Opera-Browser-Operator; 1.0) [aid=opera-operator]
AI-Agent-Capabilities: cap:browse
Example: Arc Max
AI-Agent: Arc/unknown (Arc-Max; 1.5) [aid=arc-max]
AI-Agent-Capabilities: cap:browse
Rule: AAIP MUST be attached at the network boundary that actually emits requests.
| Browser Stack | Implementation |
|---|---|
| Puppeteer/Playwright-like | Attach as extra request headers (sent with every request) |
| Browser extension | Inject via webRequest.onBeforeSendHeaders |
| Proxy-based | Inject headers at proxy layer |
| Cannot set headers | Use fallback: WebSocket first message, proxy injection |
Puppeteer note: Extra headers are sent with every request and header names are lowercased.
// Puppeteer / internal automation
await page.setExtraHTTPHeaders({
'ai-agent': 'Perplexity/sonar-2 (Comet; 1.0) [aid=comet-prod]',
'ai-agent-capabilities': 'cap:browse'
});Some ecosystems (e.g., Operator) already authenticate agent requests using HTTP message signatures. AAIP pairs well with this:
AI-Agent: OpenAI/GPT-5.2 (Operator; 2026.01) [aid=operator-prod]
AI-Agent-Signature: ed25519=<signature>See VERIFICATION.md for signature and JWT options.
- Use accurate vendor/model names
- Keep
aidstable across versions - Update application version with releases
- Use coarse capability levels by default
- Include identity on all external requests
- Impersonate other vendors
- Include user identifiers
- Over-disclose capabilities
- Use empty brackets
[]or{} - Change
aidfrequently
AAIP v2 separates these concerns:
| Layer | Header | Purpose |
|---|---|---|
| Identity | AI-Agent |
Stable, always safe to log |
| Capabilities | AI-Agent-Capabilities |
Optional, may be sensitive |
Why?
- Identity rarely changes; capabilities may vary
- Logging identity is low-risk; capabilities may aid attackers
- Rate limits may depend on identity, not capabilities
| Value | Meaning | Example |
|---|---|---|
unknown |
Not available | (App; unknown) |
redacted |
Intentionally hidden | (App; redacted) |
# Model not introspectable
AI-Agent: Vendor/unknown (MyApp; 1.0)
# Version hidden for security
AI-Agent: OpenAI/GPT-5.2 (SecretApp; redacted) [aid=secret-001]
# Orchestrator
AI-Agent: OpenAI/GPT-5.2 (Orchestrator; 1.0) [aid=orch-001]
# Worker (delegated)
AI-Agent: Anthropic/claude-4.5 (Worker; 1.0) [aid=work-001] [delegated=orch-001]
AI-Agent: OpenAI/GPT-5.2 (SaaS; 2.0) [aid=saas-001] [behalf=customer-tenant]
import re
def parse_ai_agent(header):
pattern = r'^([^/]+)/([^\s(]+)\s*\(([^;]+);\s*([^)]+)\)(?:\s*\[aid=([^\]]+)\])?'
match = re.match(pattern, header)
if match:
return {
'vendor': match.group(1),
'model': match.group(2),
'app': match.group(3),
'version': match.group(4),
'aid': match.group(5)
}
return None
# Usage
identity = parse_ai_agent("OpenAI/GPT-5.2 (MyApp; 1.0) [aid=atlas-prod]")
# {'vendor': 'OpenAI', 'model': 'GPT-5.2', 'app': 'MyApp', 'version': '1.0', 'aid': 'abc123'}app.use((req, res, next) => {
const aiAgent = req.headers['ai-agent'];
if (aiAgent) {
console.log(`AI request: ${aiAgent}`);
req.isAiAgent = true;
}
next();
});For trusted identity, use verification extensions (see VERIFICATION.md):
| Mechanism | Use Case |
|---|---|
| Signed headers | Simple verification |
| JWT assertions | Enterprise IdP integration |
| mTLS | Service-to-service |
Q: Can I omit the agent ID?
A: Yes, aid= is optional. But it helps with stable analytics.
Q: What if capabilities change per request?
A: Send appropriate capabilities per request. Identity stays stable.
Q: Should I include User-Agent too?
A: Yes. Keep User-Agent for runtime; AI-Agent supplements it.
See FAQ.md for more questions.