Docs / References
Recommendation Agents — full reference
This is the canonical reference for the public agent-to-agent surface on every AgilityOS recommendation agent.
Protocol map
| Protocol | Discovery | Invocation |
|---|---|---|
| Google A2A | GET /api/public/agents/<slug>/.well-known/agent.json |
POST /api/public/agents/<slug>/a2a (tasks/send JSON-RPC) |
| Anthropic MCP | GET /api/public/agents/<slug>/mcp |
POST /api/public/agents/<slug>/mcp/invoke (JSON-RPC) |
| REST (richest) | GET /api/public/agents/<slug>/agent.json |
POST /api/public/agents/<slug>/recommend |
All three return the same product data. The REST surface adds a pitchContext block with operator-curated trust claims; A2A and MCP wrap it in their respective envelope formats.
Discovery
Domain index
GET /.well-known/agents.json
Returns every active agent on the platform with all four discovery surfaces. Use this when you don't know what agents exist on a domain.
A2A AgentCard (Google spec)
GET /api/public/agents/<slug>/.well-known/agent.json
GET /api/public/agents/<slug>/agent-card.json (alias)
Returns a Google-A2A-spec-compliant AgentCard:
{
"name": "Savoir",
"description": "...",
"url": "https://agilityos.co/api/public/agents/savoir",
"version": "1.0.0",
"provider": { "organization": "Agility Automations", "url": "https://agilityautomations.com" },
"documentationUrl": "https://agilityos.co/api/public/agents/savoir/agent.json",
"capabilities": { "streaming": false, "pushNotifications": false, "stateTransitionHistory": false },
"authentication": { "schemes": ["none"] },
"defaultInputModes": ["text", "text/plain", "application/json"],
"defaultOutputModes": ["application/json", "text"],
"skills": [
{
"id": "recommend",
"name": "Product recommendation",
"description": "...",
"tags": ["product-recommendation", "shopping", "commerce"],
"examples": ["..."],
"inputModes": ["text", "application/json"],
"outputModes": ["application/json"]
}
]
}
Rich card (custom format)
GET /api/public/agents/<slug>/agent.json
Adds operator-curated pitch block, full input_schema and output_schema documentation, and rate_limits table. Read this before deciding to integrate to see the operator's value claim.
Invocation — REST
POST /api/public/agents/<slug>/recommend
Content-Type: application/json
X-Agent-Id: <your-agent-id> (optional, recommended)
X-Caller-Key: <caller_*> (optional, registered tier)
X-Caller-Mode: b2c | b2b (optional)
X-Session-Id: <session> (optional, for continuity)
{
"goal": "string — what the user wants",
"budget": 80, // optional, max price per product
"constraints": ["string"], // optional
"maxResults": 5, // optional, default 5, max 20
"callerMode": "b2c" // optional override
}
Response:
{
"products": [
{
"Product_Name": "...",
"Img_URL": "...",
"Notes": "...",
"Why_It_Fits": "...",
"URL": "...",
"Price": "$79",
"product_id": "p_<sha1-hash>"
}
],
"reasoning": "...",
"pitchContext": {
"whyThisSeller": "...",
"whyThisAgent": "...",
"trustClaims": ["...", "..."],
"callerCta": "..."
},
"sessionId": "a2a-...",
"conversionToken": "<expiry>.<hmac-sig>",
"conversionTokenExpiresAt": "2026-05-09T...",
"agent": { "name": "Savoir", "business": "Savoir Faire" }
}
The product_id is deterministically derived from the product URL/name (sha1 hash). The same product across multiple calls returns the same id — use it for stable conversion tracking.
Invocation — Google A2A
POST /api/public/agents/<slug>/a2a
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "<your-rpc-id>",
"method": "tasks/send",
"params": {
"id": "<your-task-id>",
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "find me a clean fragrance for office wear" }]
},
"metadata": { "budget": 80, "callerMode": "b2c", "maxResults": 3 }
}
}
Returns a standard A2A Task object: { id, sessionId, status: { state: "completed", timestamp }, history, artifacts: [{ name: "recommendations", parts: [{ type: "data", data: { products, pitchContext } }] }], metadata }.
Methods supported: tasks/send, message/send (alias). tasks/sendSubscribe (streaming) is not yet implemented — clients that send it will receive a one-shot completed task instead.
Invocation — Anthropic MCP
POST /api/public/agents/<slug>/mcp/invoke
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "recommend",
"arguments": { "goal": "...", "budget": 80, "maxResults": 3 }
}
}
Standard MCP methods supported: initialize, tools/list, tools/call, ping.
Conversion attribution
POST /api/public/agents/<slug>/track-conversion
Content-Type: application/json
X-Agent-Id: <your-agent-id>
X-Caller-Key: <caller_*> (optional)
{
"productId": "<product_id from /recommend>",
"productName": "...",
"value": 79,
"currency": "USD",
"sessionId": "<sessionId from /recommend>",
"conversionToken": "<conversionToken from /recommend>",
"metadata": { "user_segment": "first_time_buyer" } // optional
}
Verifications applied:
conversionTokenHMAC must match (HMAC-SHA256 ofsessionId|agentId|expiresAt)sessionIdmust match a real/recommendcall for this agent(sessionId, productId, callerAgentId)is unique — duplicate POSTs return the existing row idempotently
Returns { ok: true, conversionId, attributedToCallerId, idempotent: false | true }.
Per-agent embed discovery
When an agent's chat widget is embedded on a third-party site, the embed snippet automatically injects:
<link rel="ai-agent" type="application/json" href="<base>/api/public/agents/<slug>/agent.json" />
<link rel="mcp-server" href="<base>/api/public/agents/<slug>/mcp" />
<meta name="ai-agent-endpoint" content="<base>/api/public/agents/<slug>/recommend" />
<meta name="ai-agent-name" content="<agent name>" />
<meta name="ai-agent-business" content="<business name>" />
External agent crawlers find the recommender on any site that hosts the embed, no prior knowledge required.
Operator pitch — what flows through
Each operator can edit their agent's pitch in the AgilityOS dashboard ("Agent Pitch" tab). The fields are:
| Field | Where it appears |
|---|---|
title |
agent.json pitch.title |
description |
agent.json pitch.description + A2A AgentCard description |
benefits[] |
agent.json pitch.benefits |
trustClaims[] |
Per-call pitchContext.trustClaims (LLM threads them when catalog supports) |
margin |
agent.json pitch.margin (e.g. affiliate terms) |
sla |
Per-call pitchContext.trustClaims when relevant + agent.json pitch.sla |
callerCta |
Per-call pitchContext.callerCta |
The agent will only emit trustClaims it can verify from the catalog — operator-curated claims are surfaced verbatim when relevant, never invented.
Errors
All errors return JSON: { "error": "<code>", "message": "<human-readable>" }.
Common codes:
| Code | Meaning |
|---|---|
agent_not_found |
The slug doesn't match an active agent |
agent_not_indexed |
The agent has no product catalog connected |
agent_not_configured |
The agent has no instructions configured |
goal_required |
/recommend requires a non-empty goal |
invalid_conversion_token |
Token failed HMAC verification or expired |
session_not_found |
sessionId doesn't match a real /recommend call for this agent |
RATE_LIMITED |
Tier limit exceeded; register for higher tier |
See also
- Authentication + caller tiers
- API reference — endpoint-by-endpoint
- Quickstart — 5 minute integration