Skip to content
AGNT

Backend · Core modules

A2A protocol.

The AGP (Agent Gateway Protocol) envelope schema, the four supported intents, and the envelope state machine. Every field below maps 1:1 to the real AGPEnvelope pydantic model in app/routers/a2a_public.py.

What it is

A2A is the protocol external agents use to talk to AGNT. It's mounted at /a2a/v1, requires a Bearer token from an issued ApiKey, and exchanges a single pydantic envelope shape called AGPEnvelope. NemoClaw, Hermes-Kai, OpenClaw and other developer agents all speak this dialect.

The envelope

Every request body is an AGPEnvelope. Note the from_agent field is aliased to the wire name from because from is a Python keyword.

pythonapp/routers/a2a_public.py — AGPEnvelope
class AGPEnvelope(BaseModel):
    """Agent Gateway Protocol envelope — the single message shape on /a2a/v1."""

    model_config = ConfigDict(populate_by_name=True)

    protocol: str = "openclaw/a2a/v1"
    from_agent: str = Field(..., alias="from")
    to: str = "network:agnt"
    intent: str
    payload: dict[str, Any] = Field(default_factory=dict)
    agent_context: dict[str, Any] = Field(default_factory=dict)
    ttl_ms: int = 5000
    timestamp: str = ""

Fields

FieldTypeDefaultPurpose
protocolstringopenclaw/a2a/v1Protocol version discriminator.
fromstringrequiredSender agent ID. Aliased from from_agent in Python.
tostringnetwork:agntDestination. The network handles routing from here.
intentstringrequiredOne of network.ping, booking.search, venue.list, booking.confirm.
payloadobject{}Intent-specific arguments.
agent_contextobject{}Free-form metadata the sender wants echoed.
ttl_msinteger5000Soft TTL hint for queues downstream.
timestampISO-8601 string""Wall-clock timestamp at send. Optional on the wire.

Supported intents

Four intents are currently dispatched:

IntentTierHandlerPurpose
network.pingfree+handle_pingHealth + active venue count + tier echo.
booking.searchfree+handle_searchFull-text search over venues with optional location filter. Results cached 5 min.
venue.listfree+handle_venue_listList venues by category + location, up to 100.
booking.confirmpro / enterprisehandle_confirmCreate a VenueBooking row and a metered ApiKeyUsageEvent. Priced at $0.25 per call.

Examples

network.ping

httpPOST /a2a/v1 — network.ping
POST /a2a/v1 HTTP/1.1
Authorization: Bearer agnt_live_...
Content-Type: application/json

{
  "protocol": "openclaw/a2a/v1",
  "from": "nemoclaw:hello",
  "to": "network:agnt",
  "intent": "network.ping",
  "payload": {},
  "ttl_ms": 5000,
  "timestamp": "2026-04-11T12:00:00Z"
}

HTTP/1.1 200 OK
{
  "status": "connected",
  "venues": 134,
  "tier": "free",
  "agent_id": "nemoclaw",
  "network_version": "1.0"
}
httpPOST /a2a/v1 — booking.search
POST /a2a/v1 HTTP/1.1
Authorization: Bearer agnt_live_...
Content-Type: application/json

{
  "protocol": "openclaw/a2a/v1",
  "from": "nemoclaw:hello",
  "intent": "booking.search",
  "payload": {
    "query": "sunset dinner",
    "location": "canggu",
    "limit": 5
  },
  "ttl_ms": 5000,
  "timestamp": "2026-04-11T12:00:00Z"
}

HTTP/1.1 200 OK
{
  "venues": [
    {
      "id": "b9...",
      "name": "La Brisa",
      "slug": "la-brisa",
      "category": "restaurant",
      "location": "echo-beach",
      "agent_id": "venue:la-brisa",
      "is_live": true,
      "referral_fee_pct": 0.07
    }
  ],
  "fee_pct": 7,
  "expires_at": "2026-04-11T12:05:00Z",
  "query": "sunset dinner"
}

Envelope state machine

Internal AGNT bookings that route through ClawPulse carry an EnvelopeStatus that transitions through a strict state machine. Illegal transitions raise ValueError at the application boundary before they hit the DB check constraint.

pythonapp/core/a2a_enums.py — EnvelopeStatus
class EnvelopeStatus(StrEnum):
    PENDING = "pending"
    DISPATCHED = "dispatched"
    DISPATCH_FAILED = "dispatch_failed"
    ACCEPTED = "accepted"
    REJECTED = "rejected"
    COMPLETED = "completed"
    NO_SHOW = "no_show"
    EXPIRED = "expired"

Legal transitions

pythonapp/core/a2a_enums.py — ENVELOPE_TRANSITIONS
ENVELOPE_TRANSITIONS: dict[EnvelopeStatus, frozenset[EnvelopeStatus]] = {
    EnvelopeStatus.PENDING: frozenset({EnvelopeStatus.DISPATCHED, EnvelopeStatus.EXPIRED}),
    EnvelopeStatus.DISPATCHED: frozenset({
        EnvelopeStatus.ACCEPTED,
        EnvelopeStatus.REJECTED,
        EnvelopeStatus.DISPATCH_FAILED,
    }),
    EnvelopeStatus.DISPATCH_FAILED: frozenset({EnvelopeStatus.DISPATCHED}),  # allow retry
    EnvelopeStatus.ACCEPTED: frozenset({EnvelopeStatus.COMPLETED, EnvelopeStatus.NO_SHOW}),
    # Terminal — no outgoing transitions
    EnvelopeStatus.REJECTED: frozenset(),
    EnvelopeStatus.COMPLETED: frozenset(),
    EnvelopeStatus.NO_SHOW: frozenset(),
    EnvelopeStatus.EXPIRED: frozenset(),
}

REJECTED, COMPLETED, NO_SHOW, and EXPIREDare terminal — once an envelope lands in any of those states it never moves again. DISPATCH_FAILEDis special: it's the only error state that allows a retry back to DISPATCHED.

Authentication

Every request must present a Bearer token scoped to an ApiKey row. middleware/agent_auth.py (verify_agent_key) resolves the token, records last_used_at, and returns the key. Tier enforcement happens in the handler: booking.confirm raises 403 if the key is not pro or enterprise.

Metrics

Every dispatch increments the a2a_requests_total Prometheus counter with labels {intent, tier, status}. Monitor the status=error slice and the a2a_metered_events_total counter to detect billing drift.

Public status endpoint

GET /a2a/v1/status is unauthenticated and returns the current venue count, the number of agents active in the last 5 minutes, and the network version. External agents use it as a cheap health check before opening a session.

Related