Skip to main content

A2A Protocol Integration

What is A2A and Why Should I Use It?​

A2A (Agent-to-Agent) is a standardized way for AI agents to talk to each other. Think of it like a universal translator that allows different AI systems to communicate seamlessly.

The Problem A2A Solves​

Imagine you have:

  • A customer service AI from ToothFairyAI
  • A scheduling AI from another platform
  • An analytics AI running in your own system

Without A2A, these systems can't easily work together. Each has its own API format, authentication method, and way of tracking conversations.

A2A provides a universal standard so any A2A-compatible agent can communicate with any other A2A-compatible agent - regardless of who built them.

Real-World Use Cases​

ScenarioHow A2A Helps
Multi-Agent WorkflowsYour orchestrator AI can delegate tasks to specialized ToothFairyAI agents and get structured responses back
External System IntegrationConnect ToothFairyAI agents to your existing AI infrastructure using a standard protocol
Agent MarketplacesDiscover and use agents from different providers through standardized agent cards
Cross-Platform AutomationBuild automation pipelines that span multiple AI platforms

When to Use A2A vs Regular API​

Use CaseRecommendation
Simple chatbot widget on your websiteUse the Widget or REST API
Single agent, straightforward Q&AUse the REST API
Multi-agent orchestrationUse A2A ✓
Integration with other A2A-compatible systemsUse A2A ✓
Building an AI agent that calls other AI agentsUse A2A ✓
Standardized task tracking across multiple AI platformsUse A2A ✓
Quick Decision

If you just need to send messages to a ToothFairyAI agent and get responses, the REST API is simpler. Use A2A when you need standardized inter-agent communication or multi-agent orchestration.


Quick Start Guide​

Follow these steps to make your first A2A request:

Step 1: Get Your Credentials​

You need two things:

  1. API Key - Found in your ToothFairyAI workspace settings
  2. Agent ID - The UUID of the agent you want to communicate with

Step 2: Discover the Agent​

First, verify your agent is available and check its capabilities:

curl -H "x-api-key: YOUR_API_KEY" \
"https://ais.toothfairyai.com/.well-known/agent.json?agentId=YOUR_AGENT_ID"

This returns an Agent Card describing what the agent can do.

Step 3: Send Your First Message​

curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"text": "Hello! What can you help me with?"}]
},
"configuration": {
"agentId": "YOUR_AGENT_ID"
}
},
"id": 1
}'

You'll receive a Task object with an id. Save this ID for follow-up messages.

Step 4: Continue the Conversation​

To send follow-up messages, include the task ID:

curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"id": "TASK_ID_FROM_STEP_3",
"role": "user",
"parts": [{"text": "Tell me more about that"}]
}
},
"id": 2
}'

Step 5: Retrieve the Full Conversation​

To get all messages and any generated artifacts:

curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/get",
"params": {
"taskId": "TASK_ID_FROM_STEP_3"
},
"id": 3
}'

Understanding Key Concepts​

Before diving into the technical details, understand these core concepts:

Tasks = Conversations​

In A2A terminology, a Task is equivalent to a conversation or chat session. When you send a message:

  1. A new Task is created (or an existing one is continued)
  2. The Task tracks all messages between you and the agent
  3. The Task has a lifecycle: active → completed (or failed/canceled)

Messages = Chat Messages​

Messages are the individual exchanges within a Task. Each message has:

  • A role: user (you) or agent (the AI)
  • Parts: The content (text, files, or structured data)

Artifacts = Generated Outputs​

When an agent creates something tangible (an image, chart, document), it's called an Artifact. These are attached to the Task.

Agent Cards = Agent Profiles​

An Agent Card is a standardized description of an agent's capabilities, like a profile or resume. It tells you what the agent can do before you interact with it.


Endpoints​

Connect to ToothFairyAI's A2A server using these regional endpoints:

RegionEndpoint
Australiahttps://ais.toothfairyai.com
Europehttps://ais.eu.toothfairyai.com
United Stateshttps://ais.us.toothfairyai.com

Authentication​

All requests require your API key in the header:

POST / HTTP/1.1
Host: ais.toothfairyai.com
Content-Type: application/json
x-api-key: your-api-key
Subscription Required

A2A protocol access is available to all paid users. See API Integration for details.


JSON-RPC Methods Reference​

A2A uses JSON-RPC 2.0 format. Every request follows this structure:

{
"jsonrpc": "2.0",
"method": "method_name",
"params": { ... },
"id": 1
}

message/send​

Send a message and receive a Task response.

Starting a new conversation:

{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [
{ "text": "Analyze the quarterly sales report" }
]
},
"configuration": {
"agentId": "your-agent-uuid",
"taskName": "Sales Analysis"
}
},
"id": 1
}

Continuing an existing conversation:

{
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"message": {
"id": "existing-task-id",
"role": "user",
"parts": [
{ "text": "Can you compare with last quarter?" }
]
}
},
"id": 2
}

Response:

{
"jsonrpc": "2.0",
"result": {
"id": "task-uuid-123",
"name": "Sales Analysis",
"contextId": "task-uuid-123",
"state": "completed",
"status": "completed",
"messages": [
{
"role": "user",
"parts": [{ "text": "Analyze the quarterly sales report" }]
},
{
"role": "agent",
"parts": [{ "text": "Based on the quarterly sales data, I can see..." }]
}
],
"created_at": "2025-01-25T10:30:00Z",
"updated_at": "2025-01-25T10:35:00Z"
},
"id": 1
}

message/stream​

For real-time streaming responses (useful for long outputs):

{
"jsonrpc": "2.0",
"method": "message/stream",
"params": {
"message": {
"role": "user",
"parts": [{ "text": "Write a detailed analysis" }]
},
"configuration": {
"agentId": "your-agent-uuid"
}
},
"id": 1
}

Response: Server-Sent Events (SSE) stream:

data: {"type": "task_initial", "taskId": "task-uuid"}
data: {"type": "task_status_update", "status": "working", "progress": 1}
data: {"type": "task_status_update", "status": "completed", "final": true}
data: {"type": "task_complete", "task": {...}}
data: {"type": "stream_end"}

tasks/get​

Retrieve a task's current state, messages, and artifacts:

{
"jsonrpc": "2.0",
"method": "tasks/get",
"params": {
"taskId": "task-uuid-123",
"historyLength": 10
},
"id": 1
}
ParameterTypeDescription
taskIdstringRequired. The task ID
historyLengthnumberLimit number of messages returned

tasks/list​

List all tasks with optional filtering:

{
"jsonrpc": "2.0",
"method": "tasks/list",
"params": {
"pageSize": 20,
"status": "completed"
},
"id": 1
}
ParameterTypeDescription
pageSizenumberMax results (1-100, default: 50)
statusstringFilter: active, completed, failed, canceled
includeArtifactsbooleanInclude artifacts in response

Response:

{
"jsonrpc": "2.0",
"result": {
"tasks": [...],
"nextPageToken": "",
"pageSize": 20,
"totalSize": 45
},
"id": 1
}

tasks/cancel​

Cancel a running task:

{
"jsonrpc": "2.0",
"method": "tasks/cancel",
"params": {
"taskId": "task-uuid-123"
},
"id": 1
}

agent/getExtendedCard​

Get detailed information about an agent's capabilities:

{
"jsonrpc": "2.0",
"method": "agent/getExtendedCard",
"params": {
"agentId": "your-agent-uuid"
},
"id": 1
}

Task Lifecycle​

Tasks follow a defined state machine:

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ submitted │ ──▶ │ working │ ──▶ │ completed │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ failed │
│ canceled │
└─────────────┘

States​

StateDescription
activeTask is being processed
completedTask finished successfully
failedAn error occurred
canceledUser canceled the task

Statuses​

StatusWhen
submittedTask received, queued for processing
workingAgent is actively processing
input_requiredAgent needs more information
completedSuccessfully finished

Code Examples​

Python​

import requests

class ToothFairyA2A:
def __init__(self, api_key: str, region: str = "au"):
regions = {
"au": "https://ais.toothfairyai.com",
"eu": "https://ais.eu.toothfairyai.com",
"us": "https://ais.us.toothfairyai.com"
}
self.base_url = regions.get(region, regions["au"])
self.headers = {
"Content-Type": "application/json",
"x-api-key": api_key
}

def _request(self, method: str, params: dict) -> dict:
response = requests.post(
f"{self.base_url}/",
headers=self.headers,
json={
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": 1
}
)
return response.json()

def send_message(self, agent_id: str, text: str, task_id: str = None) -> dict:
"""Send a message to an agent. Returns the task."""
params = {
"message": {
"role": "user",
"parts": [{"text": text}]
},
"configuration": {"agentId": agent_id}
}
if task_id:
params["message"]["id"] = task_id

return self._request("message/send", params)

def get_task(self, task_id: str) -> dict:
"""Get task details including all messages."""
return self._request("tasks/get", {"taskId": task_id})

def list_tasks(self, page_size: int = 20) -> dict:
"""List all tasks."""
return self._request("tasks/list", {"pageSize": page_size})

def cancel_task(self, task_id: str) -> dict:
"""Cancel a running task."""
return self._request("tasks/cancel", {"taskId": task_id})


# Example usage
client = ToothFairyA2A(api_key="your-api-key")

# Start a conversation
result = client.send_message(
agent_id="your-agent-id",
text="What can you help me with?"
)
task_id = result["result"]["id"]
print(f"Task created: {task_id}")

# Continue the conversation
result = client.send_message(
agent_id="your-agent-id",
text="Tell me more about document analysis",
task_id=task_id
)

# Get the full conversation
task = client.get_task(task_id)
for msg in task["result"]["messages"]:
role = msg["role"]
text = msg["parts"][0].get("text", "")
print(f"{role}: {text[:100]}...")

JavaScript/TypeScript​

class ToothFairyA2A {
private baseUrl: string;
private headers: Record<string, string>;

constructor(apiKey: string, region: 'au' | 'eu' | 'us' = 'au') {
const regions = {
au: 'https://ais.toothfairyai.com',
eu: 'https://ais.eu.toothfairyai.com',
us: 'https://ais.us.toothfairyai.com'
};
this.baseUrl = regions[region];
this.headers = {
'Content-Type': 'application/json',
'x-api-key': apiKey
};
}

private async request(method: string, params: any): Promise<any> {
const response = await fetch(`${this.baseUrl}/`, {
method: 'POST',
headers: this.headers,
body: JSON.stringify({
jsonrpc: '2.0',
method,
params,
id: Date.now()
})
});
return response.json();
}

async sendMessage(agentId: string, text: string, taskId?: string) {
const params: any = {
message: {
role: 'user',
parts: [{ text }]
},
configuration: { agentId }
};
if (taskId) params.message.id = taskId;
return this.request('message/send', params);
}

async getTask(taskId: string) {
return this.request('tasks/get', { taskId });
}

async listTasks(pageSize = 20) {
return this.request('tasks/list', { pageSize });
}

async cancelTask(taskId: string) {
return this.request('tasks/cancel', { taskId });
}
}

// Example usage
const client = new ToothFairyA2A('your-api-key');

// Start conversation
const result = await client.sendMessage('agent-id', 'Hello!');
const taskId = result.result.id;

// Continue conversation
await client.sendMessage('agent-id', 'Tell me more', taskId);

// Get full conversation
const task = await client.getTask(taskId);
console.log(task.result.messages);

cURL Quick Reference​

# Send a new message
curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"jsonrpc":"2.0","method":"message/send","params":{"message":{"role":"user","parts":[{"text":"Hello"}]},"configuration":{"agentId":"AGENT_ID"}},"id":1}'

# Get task details
curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tasks/get","params":{"taskId":"TASK_ID"},"id":1}'

# List tasks
curl -X POST "https://ais.toothfairyai.com/" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tasks/list","params":{"pageSize":10},"id":1}'

Error Handling​

Common Errors​

ErrorCauseSolution
Missing x-api-key headerNo API key providedAdd x-api-key header
Invalid credentialsWrong API keyCheck your API key in workspace settings
agentId in configuration for new tasksMissing agent IDInclude agentId in configuration
Task not foundInvalid task IDVerify the task ID exists

Error Response Format​

{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"missing": "agentId in configuration for new tasks"
}
},
"id": 1
}

JSON-RPC Error Codes​

CodeMeaning
-32700Invalid JSON
-32600Invalid request format
-32601Unknown method
-32602Invalid parameters
-32603Internal server error
-32000Task not found

Best Practices​

Do's​

  • ✅ Store task IDs for multi-turn conversations
  • ✅ Check task state before sending follow-ups
  • ✅ Use streaming for long-running tasks
  • ✅ Implement retry logic for transient errors

Don'ts​

  • ❌ Don't expose API keys in client-side code
  • ❌ Don't send follow-ups to completed/canceled tasks
  • ❌ Don't ignore error responses

Troubleshooting​

"Task not found" Error​

Possible causes:

  1. The task ID is incorrect
  2. The task belongs to a different workspace
  3. The task was deleted

Solution: Verify the task ID and ensure you're using the correct API key for that workspace.

Empty Messages Array​

Possible cause: The task is still processing.

Solution: Check the task state. If it's active with status working, wait and poll again.

Agent Not Responding​

Possible causes:

  1. Invalid agent ID
  2. Agent is disabled
  3. Agent doesn't have the required capabilities

Solution: Use agent/getExtendedCard to verify the agent exists and check its capabilities.


Need Help?