Skip to main content

WebSocket Messages Reference

This is a comprehensive reference of every message type that flows through Relay's WebSocket connections.

Message Direction Legend

  • App → Relay: Apps send these messages to Relay
  • Relay → App: Relay sends these to apps
  • Agent → Relay: Agents send these to Relay
  • Relay → Agent: Relay sends these to agents
  • Bidirectional: Either direction

App-to-Relay Messages

Event

Send an event to an agent.

Direction: App → Relay

Schema:

{
"type": "event",
"agent_id": "string (required)",
"thread_id": "string (required)",
"payload": "object (required, max 64KB)"
}

Fields:

  • type: Always "event"
  • agent_id: Target agent (e.g., "athena", "klyve")
  • thread_id: Thread/conversation ID (e.g., "task-123", "lesson-101")
  • payload: Application-specific data, forwarded untouched to agent

Example:

{
"type": "event",
"agent_id": "athena",
"thread_id": "task-123",
"payload": {
"event": "comment.mention",
"message": "@athena summarize this",
"task_id": "task-123",
"task_title": "Q2 Roadmap"
}
}

Discover

Request list of available agents.

Direction: App → Relay

Schema:

{
"type": "discover"
}

Response: Agents message

Example:

{ "type": "discover" }

Ping

Heartbeat request.

Direction: Bidirectional

Schema:

{
"type": "ping"
}

Response: Pong message

Example:

{ "type": "ping" }

Relay-to-App Messages

Accepted

Event has been accepted for delivery.

Direction: Relay → App

Schema:

{
"type": "accepted",
"event_id": "string",
"agent_id": "string",
"session_key": "string",
"status": "accepted"
}

Fields:

  • event_id: Relay-generated unique ID for this event. Your app does NOT generate this — Relay creates it and returns it here. Use it to correlate all future token, reply, and error messages for this event.
  • agent_id: Which agent received it
  • session_key: Internal session identifier (for reference)
  • status: Always "accepted"

Example:

{
"type": "accepted",
"event_id": "evt_k9p2m",
"agent_id": "athena",
"session_key": "relay:athena:portal:task-123",
"status": "accepted"
}

Token

Streaming token from agent.

Direction: Relay → App

Schema:

{
"type": "token",
"event_id": "string",
"agent_id": "string",
"token": "string"
}

Fields:

  • event_id: Matches the original event
  • agent_id: Which agent generated it
  • token: Text fragment (typically 1-5 words)

Multiple tokens will arrive as separate messages:

{ "type": "token", "event_id": "evt_k9p2m", "agent_id": "athena", "token": "Here" }
{ "type": "token", "event_id": "evt_k9p2m", "agent_id": "athena", "token": "'s the" }
{ "type": "token", "event_id": "evt_k9p2m", "agent_id": "athena", "token": " summary" }

Reply

Final response from agent.

Direction: Relay → App

Schema:

{
"type": "reply",
"event_id": "string",
"agent_id": "string",
"thread_id": "string",
"reply": "string",
"payload": "object",
"metadata": {
"tokens_used": "number",
"model": "string",
"latency_ms": "number",
"session_key": "string"
}
}

Fields:

  • event_id: Matches the original event
  • agent_id: Which agent replied
  • thread_id: Original thread_id from event
  • reply: Complete response text
  • payload: Echo of original payload
  • metadata.tokens_used: Token count
  • metadata.model: Model that generated response
  • metadata.latency_ms: End-to-end latency
  • metadata.session_key: Session identifier

Example:

{
"type": "reply",
"event_id": "evt_k9p2m",
"agent_id": "athena",
"thread_id": "task-123",
"reply": "Here's the summary of the Q2 Roadmap Review...",
"payload": { ... },
"metadata": {
"tokens_used": 1500,
"model": "claude-sonnet",
"latency_ms": 2300,
"session_key": "relay:athena:portal:task-123"
}
}

Error

Error response for an event.

Direction: Relay → App

Schema:

{
"type": "error",
"event_id": "string | null",
"agent_id": "string",
"error": "string",
"code": "string"
}

Fields:

  • event_id: Matches the original event (may be null for handshake errors)
  • agent_id: Which agent had the error
  • error: Human-readable description
  • code: Error code (see Error Codes)

Example:

{
"type": "error",
"event_id": "evt_k9p2m",
"agent_id": "athena",
"error": "Agent not connected",
"code": "AGENT_OFFLINE"
}

Agents

List of available agents.

Direction: Relay → App

Schema:

{
"type": "agents",
"agents": [
{
"agent_id": "string",
"name": "string",
"description": "string"
}
]
}

Fields:

  • agents[].agent_id: Agent identifier
  • agents[].name: Display name
  • agents[].description: What the agent does

Example:

{
"type": "agents",
"agents": [
{
"agent_id": "athena_k9p2m3",
"name": "Athena",
"description": "Personal EA, general tasks"
},
{
"agent_id": "klyve_x8f4n2",
"name": "Klyve",
"description": "Technical workflows"
}
]
}

Pong

Heartbeat response.

Direction: Relay → App

Schema:

{
"type": "pong"
}

Example:

{ "type": "pong" }

Agent-to-Relay Messages

Token (Agent)

Stream token back to Relay.

Direction: Agent → Relay

Schema:

{
"type": "token",
"event_id": "string",
"token": "string"
}

Fields:

  • event_id: Echo from incoming event
  • token: Text fragment

Note: Agent version doesn't include agent_id (Relay knows from auth token)

Example:

{ "type": "token", "event_id": "evt_k9p2m", "token": "Here" }

Reply (Agent)

Final response from agent.

Direction: Agent → Relay

Schema:

{
"type": "reply",
"event_id": "string",
"content": "string",
"done": true,
"metadata": {
"tokens_used": "number",
"model": "string",
"latency_ms": "number"
}
}

Fields:

  • event_id: Echo from incoming event
  • content: Complete response text
  • done: Always true
  • metadata: Optional performance metrics

Example:

{
"type": "reply",
"event_id": "evt_k9p2m",
"content": "Here's the summary...",
"done": true,
"metadata": {
"tokens_used": 1500,
"model": "claude-sonnet",
"latency_ms": 2300
}
}

Error (Agent)

Error from agent.

Direction: Agent → Relay

Schema:

{
"type": "error",
"event_id": "string",
"error": "string",
"code": "string"
}

Fields:

  • event_id: Echo from incoming event
  • error: Human-readable description
  • code: Error code

Example:

{
"type": "error",
"event_id": "evt_k9p2m",
"error": "Session timeout",
"code": "AGENT_TIMEOUT"
}

Ping (Agent)

Heartbeat from agent.

Direction: Agent → Relay

Schema:

{
"type": "ping"
}

Relay-to-Agent Messages

Event (Relay to Agent)

Incoming event for agent.

Direction: Relay → Agent

Schema:

{
"type": "event",
"event_id": "string",
"app_id": "string",
"thread_id": "string",
"session_key": "string",
"payload": "object"
}

Fields:

  • event_id: Unique event ID (echo in replies)
  • app_id: Which app sent it (e.g., "portal")
  • thread_id: Thread from the event
  • session_key: relay:{app_id}:{thread_id} (for session continuity)
  • payload: Application payload (untouched by Relay)

Note: No agent_id — agent already knows who it is from auth token

Example:

{
"type": "event",
"event_id": "evt_k9p2m",
"app_id": "portal",
"thread_id": "task-123",
"session_key": "relay:portal:task-123",
"payload": {
"event": "comment.mention",
"message": "@athena summarize",
"task_id": "task-123"
}
}

Pong (Relay to Agent)

Heartbeat response.

Direction: Relay → Agent

Schema:

{
"type": "pong"
}

Error Codes

CodeMeaningRetryable
AGENT_OFFLINEAgent not connectedYes
AGENT_NOT_ALLOWEDApp not allowlistedNo
AGENT_TIMEOUTAgent response timeoutMaybe
PAYLOAD_TOO_LARGEEvent exceeds 64KBNo
RATE_LIMITEDRate limit exceededYes
INVALID_EVENTMalformed eventNo
RELAY_INTERNAL_ERRORServer errorYes

See Error Codes for detailed information on each.


Message Flow Examples

Happy Path (App → Agent → App)

1. App: { "type": "event", "agent_id": "athena", ... }
2. Relay → App: { "type": "accepted", "event_id": "evt_k9p2m" }
3. Relay → Agent: { "type": "event", "event_id": "evt_k9p2m", ... }
4. Agent → Relay: { "type": "token", "event_id": "evt_k9p2m", "token": "Here" }
5. Relay → App: { "type": "token", "event_id": "evt_k9p2m", "token": "Here" }
6. Agent → Relay: { "type": "token", "event_id": "evt_k9p2m", "token": "'s the" }
7. Relay → App: { "type": "token", "event_id": "evt_k9p2m", "token": "'s the" }
8. Agent → Relay: { "type": "reply", "event_id": "evt_k9p2m", "content": "..." }
9. Relay → App: { "type": "reply", "event_id": "evt_k9p2m", "reply": "..." }

Agent Offline

1. App: { "type": "event", "agent_id": "athena", ... }
2. Relay → App: { "type": "error", "code": "AGENT_OFFLINE", ... }

Agent Discovery

1. App: { "type": "discover" }
2. Relay → App: { "type": "agents", "agents": [...] }

Heartbeat

1. App: { "type": "ping" }
2. Relay → App: { "type": "pong" }

Best Practices

For Apps

  • Always store event_id from "accepted"
  • Use event_id to match tokens/replies
  • Handle all message types
  • Send heartbeats every 30-60s
  • Validate payload size < 64KB

For Agents

  • Always echo event_id in responses
  • Send tokens frequently (not buffered)
  • Include metadata in final reply
  • Handle connection errors gracefully
  • Send heartbeats every 30-60s