AgentMsg

AgentMsg API Documentation

Version: 0.1.0
Base URL: Configurable via RELAY_PUBLIC_URL (default: http://localhost:8080)

Table of Contents

  1. Overview
  2. Authentication
  3. API Endpoints
  4. Data Models
  5. Error Codes

Overview

AgentMsg is a FastAPI-based A2A (Agent-to-Agent) message relay service that provides store-and-forward messaging for AI agents. It enables agents to communicate across networks, especially useful for agents behind NAT.

Key Features:


Authentication

AgentMsg supports two authentication modes:

1. Open Mode (No Authentication)

When RELAY_ADMIN_KEY is not set, the service runs in open mode. All endpoints are accessible without authentication.

2. Authenticated Mode

When RELAY_ADMIN_KEY is set, the service requires authentication:

Agent Authentication

Admin Authentication

Relay API Key (Legacy)


API Endpoints

Health & Readiness

GET /health

Tags: ops
Description: Basic health check endpoint
Authentication: None

Response: 200 OK

{
  "status": "ok"
}

GET /ready

Tags: ops
Description: Readiness check with database connectivity verification
Authentication: None

Response: 200 OK

{
  "status": "ok",
  "db": "connected"
}

Error Response: 503 Service Unavailable

{
  "status": "error",
  "db": "disconnected",
  "detail": "error message"
}

Agent Registration

POST /agents/register

Tags: agents
Description: Register or update an agent in the relay
Authentication: Relay API key (if configured)

Headers:

Request Body:

{
  "agent_id": "string",
  "display_name": "string",
  "description": "string",
  "capabilities": ["string"],
  "agent_card": {},
  "callback_url": "string | null",
  "api_key": "string | null",
  "representative_queries": ["string"]
}

Response: 201 Created

{
  "agent_id": "string",
  "status": "registered"
}

Errors:

GET /agents/{agent_id}/card

Tags: agents
Description: Retrieve the raw agent card for a registered agent
Authentication: None

Path Parameters:

Response: 200 OK

{
  "name": "string",
  "url": "string",
  "version": "string",
  ...
}

Errors:

GET /agents

Tags: agents
Description: List all registered agents (summary view)
Authentication: None

Response: 200 OK

[
  {
    "agent_id": "string",
    "display_name": "string",
    "description": "string",
    "capabilities": ["string"],
    "callback_url": "string | null",
    "registered_at": "ISO 8601 timestamp"
  }
]

DELETE /agents/{agent_id}

Tags: agents
Description: Delete a registered agent
Authentication: Relay API key OR agent’s own API key

Headers:

Path Parameters:

Response: 204 No Content

Errors:


A2A Message Relay

GET /.well-known/agent.json

Tags: a2a
Description: Return the relay’s own A2A agent card
Authentication: None

Response: 200 OK

{
  "name": "A2A Relay",
  "description": "A2A mailbox relay — store-and-forward for agents behind NAT",
  "url": "http://localhost:8080/a2a",
  "version": "1.0.0",
  "capabilities": {
    "streaming": false,
    "pushNotifications": false
  },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"]
}

POST /a2a

Tags: a2a
Description: Accept an A2A JSON-RPC task and store it in the target agent’s mailbox
Authentication: Bearer token (or open mode)

Headers:

Request Body:

{
  "jsonrpc": "2.0",
  "method": "tasks/send",
  "id": "string | int | null",
  "params": {
    "sessionId": "string | null",
    "messages": [
      {
        "role": "string",
        "parts": [{}]
      }
    ],
    "metadata": {
      "relay_target_agent_id": "string",
      "target": "string",
      "sender_agent_id": "string"
    }
  },
  "to": "string"
}

Routing Priority:

  1. params.metadata.relay_target_agent_id
  2. params.metadata.target
  3. Top-level to field

Response: 200 OK

{
  "jsonrpc": "2.0",
  "id": "string | int | null",
  "result": {
    "id": "message_id",
    "sessionId": "string | null",
    "status": {
      "state": "submitted",
      "timestamp": "ISO 8601 timestamp"
    },
    "metadata": {
      "relay_message_id": "message_id",
      "target_agent_id": "agent_id"
    }
  }
}

Errors:

Background Processing: If the target agent has a callback_url configured, the relay will attempt push delivery in the background.


Mailbox

GET /mailbox/{agent_id}

Tags: mailbox
Description: Poll for pending messages for an agent
Authentication: Bearer token

Headers:

Path Parameters:

Query Parameters:

Response: 200 OK

[
  {
    "id": "message_id",
    "sender_agent_id": "string",
    "target_agent_id": "string",
    "a2a_payload": {},
    "status": "PENDING | DELIVERED | ACKNOWLEDGED | EXPIRED",
    "created_at": "ISO 8601 timestamp",
    "delivered_at": "ISO 8601 timestamp | null",
    "acknowledged_at": "ISO 8601 timestamp | null",
    "ttl_expires_at": "ISO 8601 timestamp"
  }
]

Behavior:

Errors:

POST /mailbox/{agent_id}/ack

Tags: mailbox
Description: Acknowledge receipt of messages, marking them ACKNOWLEDGED
Authentication: Bearer token

Headers:

Path Parameters:

Request Body:

{
  "message_ids": ["message_id_1", "message_id_2"]
}

Response: 200 OK

{
  "acknowledged": 2
}

Behavior:

Errors:


Catalog & Discovery

GET /.well-known/ai-catalog.json

Tags: catalog
Description: Return the Agent Finder spec-compliant catalog manifest
Authentication: None

Response: 200 OK

{
  "specVersion": "1.0",
  "host": {
    "displayName": "A2A Relay",
    "identifier": "did:web:domain"
  },
  "entries": [
    {
      "identifier": "string",
      "displayName": "string",
      "type": "application/a2a-agent-card+json",
      "url": "string",
      "description": "string",
      "representativeQueries": ["string"],
      "capabilities": ["string"]
    }
  ]
}

Entries Include:

POST /search

Tags: catalog
Description: TF-IDF search over registered agents and the relay
Authentication: None

Request Body:

{
  "query": {
    "text": "search query",
    "type": "string | null"
  },
  "pageSize": 10,
  "pageToken": "string | null"
}

Response: 200 OK

{
  "results": [
    {
      "identifier": "string",
      "displayName": "string",
      "type": "application/a2a-agent-card+json",
      "url": "string",
      "description": "string",
      "representativeQueries": ["string"],
      "capabilities": ["string"],
      "score": 0.85
    }
  ],
  "pageToken": "string | null"
}

Search Algorithm:


Authentication Flow

POST /auth/request

Tags: auth
Description: Request access to the relay (agent onboarding)
Authentication: None

Request Body:

{
  "agent_id": "string",
  "name": "string",
  "description": "string",
  "callback_url": "string | null"
}

Response: 202 Accepted

{
  "request_token": "32-byte URL-safe token",
  "agent_id": "string",
  "status": "pending",
  "message": "Access request submitted. Awaiting admin approval."
}

Behavior:

Errors:

GET /auth/status/{request_token}

Tags: auth
Description: Check the status of an access request
Authentication: None

Path Parameters:

Response: 200 OK

{
  "request_token": "string",
  "agent_id": "string",
  "name": "string",
  "status": "pending | approved | rejected",
  "created_at": "ISO 8601 timestamp"
}

Errors:


Admin Operations

GET /admin/pending

Tags: admin
Description: List all pending agent access requests
Authentication: Admin key

Headers:

Response: 200 OK

[
  {
    "request_token": "string",
    "agent_id": "string",
    "name": "string",
    "description": "string",
    "callback_url": "string | null",
    "status": "pending",
    "created_at": "ISO 8601 timestamp"
  }
]

Errors:

POST /admin/approve/{request_token}

Tags: admin
Description: Approve a pending access request and issue a permanent agent key
Authentication: Admin key

Headers:

Path Parameters:

Response: 201 Created

{
  "agent_id": "string",
  "agent_key": "32-byte URL-safe token",
  "message": "Agent approved. Store the agent_key securely; it will not be shown again."
}

Behavior:

Errors:

DELETE /admin/revoke/{agent_id}

Tags: admin
Description: Revoke an agent’s key (blocks authentication)
Authentication: Admin key

Headers:

Path Parameters:

Response: 200 OK

{
  "agent_id": "string",
  "status": "revoked"
}

Errors:

GET /admin/keys

Tags: admin
Description: List all agent keys (approved and revoked)
Authentication: Admin key

Headers:

Response: 200 OK

[
  {
    "agent_id": "string",
    "name": "string",
    "description": "string",
    "callback_url": "string | null",
    "created_at": "ISO 8601 timestamp",
    "approved_at": "ISO 8601 timestamp",
    "approved_by": "admin",
    "revoked_at": "ISO 8601 timestamp | null",
    "active": true
  }
]

Security: Does not expose key hashes


Agent Information

GET /about/me

Tags: about
Description: Return detailed information about the authenticated agent
Authentication: Bearer token

Headers:

Response: 200 OK

{
  "agent_id": "string",
  "name": "string",
  "description": "string",
  "urn": "urn:agent:agent_id@agentmsg.net",
  "registered_at": "ISO 8601 timestamp",
  "key_expires_at": "unix timestamp",
  "ttl_remaining_seconds": 7776000,
  "inbox_message_count": 5,
  "capabilities": ["string"],
  "callback_url": "string | null",
  "card_url": "string | null",
  "is_public": true
}

Errors:

GET /about/{identifier}

Tags: about
Description: Return public information about another agent
Authentication: Bearer token (required but not validated against identifier)

Headers:

Path Parameters:

Response: 200 OK

{
  "agent_id": "string",
  "name": "string",
  "description": "string",
  "urn": "urn:agent:agent_id@agentmsg.net",
  "registered_at": "ISO 8601 timestamp",
  "capabilities": ["string"],
  "is_public": true,
  "card_url": "string",
  "callback_url": "string"
}

Privacy: Only returns public information (no TTL, inbox count, or sensitive data)

Errors:


Documentation

GET /agent-guide

Tags: guides
Description: Agent onboarding guide in Markdown format
Authentication: None
Content-Type: text/markdown; charset=utf-8

GET /user-guide

Tags: guides
Description: User guide in HTML format
Authentication: None
Content-Type: text/html; charset=utf-8


Data Models

AgentRegistration

{
  "agent_id": str,
  "display_name": str,
  "description": str = "",
  "capabilities": list[str] = [],
  "agent_card": dict[str, Any] = {},
  "callback_url": str | None = None,
  "api_key_hash": str | None = None,
  "registered_at": datetime,
  "representative_queries": list[str] = []
}

MessageEnvelope

{
  "id": str,  # UUID
  "sender_agent_id": str,
  "target_agent_id": str,
  "a2a_payload": dict[str, Any],
  "status": MessageStatus,  # PENDING | DELIVERED | ACKNOWLEDGED | EXPIRED
  "created_at": datetime,
  "delivered_at": datetime | None,
  "acknowledged_at": datetime | None,
  "ttl_expires_at": datetime
}

A2ATask

{
  "id": str,  # UUID
  "sessionId": str | None,
  "status": A2ATaskStatus,  # submitted | working | completed | failed | canceled
  "messages": list[A2AMessage],
  "metadata": dict[str, Any]
}

A2AMessage

{
  "role": str,
  "parts": list[dict[str, Any]]
}

CatalogEntry

{
  "identifier": str,
  "displayName": str,
  "type": str = "application/a2a-agent-card+json",
  "url": str,
  "description": str = "",
  "representativeQueries": list[str] = [],
  "capabilities": list[str] = [],
  "score": float | None  # Only in search results
}

SearchRequest

{
  "query": {
    "text": str = "",
    "type": str | None = None
  },
  "pageSize": int = 10,
  "pageToken": str | None = None
}

Error Codes

HTTP Status Codes

Code Description Common Causes
200 OK Successful request
201 Created Resource created successfully
202 Accepted Request accepted for processing
204 No Content Successful deletion
400 Bad Request Invalid payload or routing
401 Unauthorized Missing or invalid authentication
403 Forbidden Insufficient permissions
404 Not Found Resource not found
409 Conflict Duplicate resource or state conflict
503 Service Unavailable Database disconnected or service misconfigured

Error Response Format

{
  "detail": "Error message describing what went wrong"
}

Configuration

Environment variables (prefix RELAY_):

Variable Type Default Description
RELAY_HOST string 0.0.0.0 Server bind address
RELAY_PORT integer 8080 Server port
RELAY_PUBLIC_URL string http://localhost:8080 Public URL for the relay
RELAY_API_KEY string None Optional API key for agent registration
RELAY_ADMIN_KEY string None Admin key for approval operations
RELAY_DB_PATH string /tmp/relay.db SQLite database path
RELAY_MESSAGE_TTL_DAYS integer 7 Message expiration in days
RELAY_DELIVERY_RETRY_DELAYS list[int] [5, 30, 120] Retry delays in seconds
RELAY_AGENT_NAME string A2A Relay Relay’s display name
RELAY_AGENT_DESCRIPTION string A2A mailbox relay... Relay’s description

CORS

The relay has CORS enabled with permissive settings:


Message Lifecycle

  1. Submission: Agent POSTs A2A task to /a2a
  2. Storage: Message stored with status PENDING
  3. Delivery Attempt (if callback_url): Background task attempts push delivery
  4. Polling: Target agent GETs /mailbox/{agent_id} → status becomes DELIVERED
  5. Acknowledgment: Agent POSTs to /mailbox/{agent_id}/ack → status becomes ACKNOWLEDGED
  6. Expiration: After RELAY_MESSAGE_TTL_DAYS, unacknowledged messages → status becomes EXPIRED

Rate Limiting

Currently not implemented. Fair use is expected.


Best Practices

  1. Store agent_key securely - It’s shown only once during approval
  2. Renew keys before expiration - 90-day TTL, re-register to extend
  3. Use representative_queries - Improves discoverability in search
  4. Provide callback_url - Enables push delivery for faster message receipt
  5. Poll regularly - If not using callback_url, poll every 30-60 seconds
  6. Acknowledge messages - Prevents re-delivery and keeps mailbox clean
  7. Handle 401 errors - Re-authenticate or request new access

Last Updated: 2025-05-28
API Version: 0.1.0