Skip to content

API Reference

Hector provides multiple API transports for interacting with agents. All APIs implement the A2A Protocol specification.


Quick Reference

Transport Port Use Case Protocol
gRPC 50051 High performance Binary/HTTP2
REST 8080 Web/Browser JSON/HTTP1
JSON-RPC 8080 Simple integration JSON-RPC 2.0
WebSocket 8080/ws Real-time streaming WebSocket
Web UI 8080 Interactive browser UI HTML/JavaScript

All HTTP-based APIs consolidated on port 8080 for simplicity.


Base URLs

HTTP (REST/JSON-RPC/WebSocket/UI): http://localhost:8080
gRPC:                               localhost:50051

Specific endpoints:
  REST API:     http://localhost:8080/v1/
  JSON-RPC:     http://localhost:8080/ (POST)
  Web UI:       http://localhost:8080/ (GET)
  WebSocket:    ws://localhost:8080/v1/agents/{agent}/stream

Authentication

All transports support JWT authentication (when enabled):

HTTP Header:

Authorization: Bearer <jwt_token>

gRPC Metadata:

authorization: Bearer <jwt_token>


REST API

Send Message

Send a single message to an agent.

Endpoint: POST /v1/agents/{agent}/message:send

Request:

{
  "message": {
    "role": "user",
    "parts": [
      {"text": "What is the capital of France?"}
    ],
    "contextId": "session-123"
  }
}

Response:

{
  "task": {
    "id": "tasks/task_abc123",
    "contextId": "session-123",
    "status": {
      "state": "completed",
      "message": "Task completed"
    },
    "result": {
      "role": "agent",
      "parts": [
        {"text": "The capital of France is Paris."}
      ],
      "messageId": "msg_xyz789"
    }
  }
}

Example:

curl -X POST http://localhost:8080/v1/agents/assistant/message:send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "message": {
      "role": "user",
      "parts": [{"text": "Hello"}],
      "contextId": "my-session"
    }
  }'

Stream Message (SSE)

Stream responses in real-time using Server-Sent Events.

Endpoint: POST /v1/agents/{agent}/message:stream

Request: Same as Send Message

Response: Server-Sent Events stream

event: message
data: {"message":{"role":"agent","parts":[{"text":"The"}],"messageId":"msg_1"}}

event: message
data: {"message":{"role":"agent","parts":[{"text":" capital"}],"messageId":"msg_1"}}

event: message
data: {"message":{"role":"agent","parts":[{"text":" of France"}],"messageId":"msg_1"}}

event: message
data: {"statusUpdate":{"taskId":"tasks/123","status":{"state":"completed"},"final":true}}

Example:

curl -N -X POST http://localhost:8080/v1/agents/assistant/message:stream \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "message": {
      "role": "user",
      "parts": [{"text": "Explain quantum computing"}],
      "contextId": "my-session"
    }
  }'

Get Agent Card

Get agent metadata and capabilities.

Endpoint: GET /v1/agents/{agent}/.well-known/agent-card.json

Per A2A Specification Section 5.3, agent cards MUST be at /.well-known/agent-card.json

Response:

{
  "agent": {
    "name": "My Assistant",
    "description": "A helpful AI assistant",
    "version": "1.0.0",
    "capabilities": ["chat", "tools", "streaming"],
    "supported_content_types": ["text"]
  }
}

Example:

# Get agent card for specific agent
curl http://localhost:8080/v1/agents/assistant/.well-known/agent-card.json

# With authentication
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/v1/agents/assistant/.well-known/agent-card.json

List Agents

List all available agents.

Endpoint: GET /v1/agents

Response:

{
  "agents": [
    {"id": "assistant", "name": "My Assistant"},
    {"id": "coder", "name": "Coding Assistant"}
  ]
}

Example:

curl http://localhost:8080/v1/agents

Get Task Status

Get status and results of an async task.

Endpoint: GET /v1/agents/{agent}/tasks/{task_id}

Query Parameters: - history_length (optional) - Number of recent messages to include

Response:

{
  "id": "task-abc123",
  "contextId": "ctx-456",
  "status": {
    "state": "TASK_STATE_COMPLETED",
    "update": {
      "role": "agent",
      "parts": [{"text": "Task completed successfully"}]
    },
    "timestamp": "2025-11-02T10:00:05Z"
  },
  "artifacts": [],
  "history": [
    {
      "messageId": "msg-1",
      "role": "user",
      "parts": [{"text": "Request"}]
    },
    {
      "messageId": "msg-2",
      "role": "agent",
      "parts": [{"text": "Response"}]
    }
  ]
}

Example:

# Get task with full history
curl http://localhost:8080/v1/agents/assistant/tasks/task-abc123

# Get task with limited history
curl http://localhost:8080/v1/agents/assistant/tasks/task-abc123?history_length=5

# CLI command (client mode)
hector task get assistant task-abc123 --url http://localhost:8080

See also: Task Management Documentation

List Tasks

List tasks with optional filtering and pagination.

Endpoint: GET /v1/agents/{agent}/tasks

Query Parameters: - context_id (optional) - Filter by context/session - status (optional) - Filter by task state - page_size (optional) - Results per page (default: 50, max: 100) - page_token (optional) - Pagination token from previous response - history_length (optional) - Messages to include per task (default: 0) - include_artifacts (optional) - Include artifacts (default: false)

Response:

{
  "tasks": [
    {
      "id": "task-1",
      "contextId": "ctx-456",
      "status": {
        "state": "TASK_STATE_COMPLETED"
      }
    },
    {
      "id": "task-2",
      "contextId": "ctx-456",
      "status": {
        "state": "TASK_STATE_WORKING"
      }
    }
  ],
  "nextPageToken": "page-2",
  "totalSize": 42
}

Example:

# List all tasks for agent
curl http://localhost:8080/v1/agents/assistant/tasks

# List with filtering
curl http://localhost:8080/v1/agents/assistant/tasks?context_id=ctx-456&page_size=10

# List with pagination
curl http://localhost:8080/v1/agents/assistant/tasks?page_size=20&page_token=page-2

Cancel Task

Cancel a running task.

Endpoint: POST /v1/agents/{agent}/tasks/{task_id}:cancel

Request Body: {} (empty JSON object)

Response:

{
  "id": "task-abc123",
  "contextId": "ctx-456",
  "status": {
    "state": "TASK_STATE_CANCELLED",
    "update": {
      "role": "agent",
      "parts": [{"text": "Task cancelled by user"}]
    },
    "timestamp": "2025-11-02T10:00:10Z"
  }
}

Example:

# Cancel via REST API
curl -X POST http://localhost:8080/v1/agents/assistant/tasks/task-abc123:cancel \
  -H "Content-Type: application/json" \
  -d '{}'

# CLI command (client mode)
hector task cancel assistant task-abc123 --url http://localhost:8080

Task States (A2A Spec Section 6.3):

State Description Type
TASK_STATE_SUBMITTED Task created and queued Initial
TASK_STATE_WORKING Task is being processed Active
TASK_STATE_COMPLETED Task finished successfully Terminal
TASK_STATE_FAILED Task failed with error Terminal
TASK_STATE_CANCELLED Task was cancelled Terminal
TASK_STATE_INPUT_REQUIRED Task needs user input (e.g., tool approval) Interrupted
TASK_STATE_REJECTED Agent rejected task Terminal
TASK_STATE_AUTH_REQUIRED Authentication required Special

Human-in-the-Loop (HITL): When a task enters TASK_STATE_INPUT_REQUIRED, it's waiting for user input (typically tool approval). Respond using the same taskId to resume execution:

# Task paused for approval
curl http://localhost:8080/v1/agents/assistant/tasks/task-abc123
# Response: {"status": {"state": "TASK_STATE_INPUT_REQUIRED", ...}}

# Respond with approval (using same taskId)
curl -X POST http://localhost:8080/v1/agents/assistant/message:send \
  -H "Content-Type: application/json" \
  -d '{
    "request": {
      "role": "user",
      "taskId": "task-abc123",
      "parts": [
        {"text": "approve"},
        {"data": {"decision": "approve"}}
      ]
    }
  }'

Checkpoint Recovery: Tasks with checkpoint recovery enabled can survive server crashes and restarts. Checkpoints are automatically created:

  • On HITL pauses (INPUT_REQUIRED state) - event-driven
  • Periodically during execution (WORKING state) - interval-based (if configured)
  • On server restart, tasks in WORKING or INPUT_REQUIRED state are automatically recovered if checkpoints exist

See: Human-in-the-Loop for complete HITL guide.
See: Checkpoint Recovery for checkpoint recovery architecture.

Complete Documentation: Tasks - Lifecycle and Management


gRPC API

Service Definition

service A2AService {
  // Send message and get response
  rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);

  // Send message and stream response
  rpc SendStreamingMessage(SendMessageRequest) returns (stream StreamResponse);

  // Get agent metadata
  rpc GetAgentCard(GetAgentCardRequest) returns (AgentCard);

  // Task management
  rpc GetTask(GetTaskRequest) returns (Task);
  rpc CancelTask(CancelTaskRequest) returns (Task);
  rpc TaskSubscription(TaskSubscriptionRequest) returns (stream StreamResponse);
}

Send Message

Request:

message SendMessageRequest {
  Message request = 1;
}

message Message {
  Role role = 1;  // ROLE_USER, ROLE_ASSISTANT
  repeated Content content = 2;
}

message Content {
  oneof content {
    string text = 1;
  }
}

Metadata:

agent-name: assistant
authorization: Bearer <token>

Example:

grpcurl -plaintext \
  -H 'agent-name: assistant' \
  -H 'authorization: Bearer $TOKEN' \
  -d '{
    "request": {
      "role": "ROLE_USER",
      "content": [{"text": "Hello"}]
    }
  }' \
  localhost:8080 \
  a2a.v1.A2AService/SendMessage

Stream Message

Example:

grpcurl -plaintext \
  -H 'agent-name: assistant' \
  -d '{
    "request": {
      "role": "ROLE_USER",
      "content": [{"text": "Explain recursion"}]
    }
  }' \
  localhost:8080 \
  a2a.v1.A2AService/SendStreamingMessage


WebSocket API

Real-time bidirectional communication.

Endpoint: ws://localhost:8080/v1/agents/{agent}/stream

Connection

const ws = new WebSocket('ws://localhost:8080/v1/agents/assistant/stream');

ws.onopen = () => {
  console.log('Connected');

  // Send message
  ws.send(JSON.stringify({
    message: {
      role: 'ROLE_USER',
      content: [{text: 'Hello'}]
    }
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

ws.onerror = (error) => {
  console.error('Error:', error);
};

ws.onclose = () => {
  console.log('Disconnected');
};

Message Format

Send:

{
  "message": {
    "role": "ROLE_USER",
    "content": [{"text": "Hello"}]
  }
}

Receive:

{
  "response": {
    "role": "ROLE_ASSISTANT",
    "content": [{"text": "Hello! How can I help?"}]
  }
}


JSON-RPC API

Simple JSON-RPC 2.0 interface with agent-scoped endpoints.

Endpoint Pattern: POST /v1/agents/{agent_id}/

All JSON-RPC requests are agent-scoped - the agent is identified by the URL path, not query parameters.

Agent-Scoped Endpoints

# Non-streaming JSON-RPC
POST /v1/agents/{agent_id}/

# Streaming JSON-RPC (SSE)
POST /v1/agents/{agent_id}/stream

Examples:

# Send message to orchestrator agent
POST http://localhost:8080/v1/agents/orchestrator/

# Streaming to assistant agent
POST http://localhost:8080/v1/agents/assistant/stream

Send Message

Request:

{
  "jsonrpc": "2.0",
  "method": "message/send",
  "params": {
    "name": "assistant",
    "message": {
      "role": "ROLE_USER",
      "content": [{"text": "Hello"}]
    }
  },
  "id": 1
}

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "response": {
      "role": "ROLE_ASSISTANT",
      "content": [{"text": "Hello! How can I help?"}]
    },
    "task_id": "task_abc123"
  },
  "id": 1
}

Examples:

# Agent-scoped JSON-RPC
curl -X POST http://localhost:8080/v1/agents/assistant/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "message/send",
    "params": {
      "request": {
        "role": "user",
        "parts": [{"text": "Hello"}]
      }
    },
    "id": 1
  }'

# Agent-scoped with session context
curl -X POST http://localhost:8080/v1/agents/orchestrator/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "message/send",
    "params": {
      "request": {
        "contextId": "session-123",
        "role": "user",
        "parts": [{"text": "Hello"}]
      }
    },
    "id": 1
  }'

Sessions

Create and manage conversation sessions.

Create Session

REST: POST /v1/agents/{agent}/sessions

Response:

{
  "session_id": "sess_abc123"
}

Send Message in Session

REST: POST /v1/agents/{agent}/sessions/{session_id}/messages

Request:

{
  "message": {
    "role": "ROLE_USER",
    "content": [{"text": "Hello"}]
  }
}

List Sessions

REST: GET /v1/agents/{agent}/sessions

Delete Session

REST: DELETE /v1/agents/{agent}/sessions/{session_id}


Error Handling

Error Response Format

{
  "error": {
    "code": "INVALID_ARGUMENT",
    "message": "Agent not found: unknown_agent",
    "details": {
      "name": "unknown_agent"
    }
  }
}

Error Codes

Code HTTP Status Description
OK 200 Success
INVALID_ARGUMENT 400 Invalid request parameters
UNAUTHENTICATED 401 Missing or invalid authentication
PERMISSION_DENIED 403 Insufficient permissions
NOT_FOUND 404 Agent or resource not found
RESOURCE_EXHAUSTED 429 Rate limit exceeded
INTERNAL 500 Internal server error
UNAVAILABLE 503 Service temporarily unavailable

Example Error Response

curl http://localhost:8080/v1/agents/unknown
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Agent not found: unknown"
  }
}

Rate Limiting

Hector supports rate limiting per agent and per user.

Headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

Rate Limit Exceeded:

{
  "error": {
    "code": "RESOURCE_EXHAUSTED",
    "message": "Rate limit exceeded. Try again in 60 seconds."
  }
}


Content Types

Supported Content

Text:

{
  "content": [{"text": "Hello"}]
}

Multiple Chunks:

{
  "content": [
    {"text": "First part"},
    {"text": "Second part"}
  ]
}

Future Support: - Images - Audio - Video - Files


Client Libraries

JavaScript/TypeScript

import { A2AClient } from '@hector/client';

const client = new A2AClient({
  baseUrl: 'http://localhost:8080',
  token: 'your-jwt-token'
});

// Send message
const response = await client.sendMessage('assistant', {
  role: 'ROLE_USER',
  content: [{text: 'Hello'}]
});

// Stream message
const stream = client.streamMessage('assistant', {
  role: 'ROLE_USER',
  content: [{text: 'Explain AI'}]
});

for await (const chunk of stream) {
  process.stdout.write(chunk.content[0].text);
}

Python

from hector_client import A2AClient

client = A2AClient(
    base_url='http://localhost:8080',
    token='your-jwt-token'
)

# Send message
response = client.send_message('assistant', {
    'role': 'ROLE_USER',
    'content': [{'text': 'Hello'}]
})

# Stream message
for chunk in client.stream_message('assistant', {
    'role': 'ROLE_USER',
    'content': [{'text': 'Explain AI'}]
}):
    print(chunk['content'][0]['text'], end='')

Go

import "github.com/kadirpekel/hector/pkg/client"

client := client.New(client.Config{
    BaseURL: "http://localhost:8080",
    Token:   "your-jwt-token",
})

// Send message
response, err := client.SendMessage(ctx, "assistant", &a2a.Message{
    Role:    a2a.ROLE_USER,
    Content: []*a2a.Content{{Text: "Hello"}},
})

// Stream message
stream, err := client.StreamMessage(ctx, "assistant", &a2a.Message{
    Role:    a2a.ROLE_USER,
    Content: []*a2a.Content{{Text: "Explain AI"}},
})

for {
    chunk, err := stream.Recv()
    if err == io.EOF {
        break
    }
    fmt.Print(chunk.Content[0].Text)
}

Health Check

Endpoint: GET /health

Response:

{
  "status": "healthy",
  "version": "0.x.x",
  "uptime": "2h15m30s"
}


Next Steps