AgilityOS

Docs / References

Plain-text version: /docs/recommendation-agents.md  ·  llms.txt

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:

  1. conversionToken HMAC must match (HMAC-SHA256 of sessionId|agentId|expiresAt)
  2. sessionId must match a real /recommend call for this agent
  3. (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