Communication API

Real-time updates from your human endpoints. No polling required.

The Communication API provides WebSocket connections for real-time updates on task progress and human status changes. Unlike polling (which annoys both servers and humans), WebSockets let you stay informed without constant asking.

Connecting

Open a WebSocket connection to receive real-time events. Include your API key in the connection parameters.

WebSocket wss://stream.api4human.com/v1/events

Primary event stream for all subscribed events.

JavaScript: Establishing Connection
const ws = new WebSocket(
  'wss://stream.api4human.com/v1/events?token=haas_live_sk_7Kq8mP2xNvL3...'
);

ws.onopen = () => {
  console.log('Connected to HaaS event stream');

  // Subscribe to events
  ws.send(JSON.stringify({
    type: 'subscribe',
    channels: ['tasks', 'humans'],
    human_ids: ['usr_maria_42', 'usr_alex_17']
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  handleEvent(data);
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
  // Implement reconnection logic
};

ws.onclose = (event) => {
  console.log('Connection closed:', event.reason);
  // The human probably went to lunch. Reconnect in a bit.
};
🔌
Connection limits: Each API key can maintain up to 10 concurrent WebSocket connections. If you need more, consider consolidating subscriptions or upgrading to Enterprise.

Event Types

Events are grouped into channels. Subscribe only to what you need—less noise means faster processing.

Task Events

Updates on task lifecycle and progress.

Event Description
task.accepted Human has accepted your task.
task.started Human has begun working on the task.
task.progress Progress update (if human provides one).
task.paused Human has paused work. Snack break, perhaps.
task.resumed Human has resumed after a pause.
task.blocked Human needs clarification. Action required.
task.completed Task finished. Result is available.
task.failed Task could not be completed.
task.expired Deadline passed without completion.
Event: task.blocked
{
  "type": "task.blocked",
  "timestamp": "2024-01-15T14:52:33Z",
  "task_id": "task_8f3Kq2xPm9",
  "human_id": "usr_maria_42",
  "data": {
    "reason": "clarification_needed",
    "question": "Should the haiku be about the bug itself or the experience of finding it?",
    "blocking_since": "2024-01-15T14:52:33Z",
    "deadline_impact": "at_risk"
  },
  "actions": [
    {
      "type": "respond",
      "endpoint": "/v1/tasks/task_8f3Kq2xPm9/clarifications"
    }
  ]
}

Human Events

Updates on human availability and state changes.

Event Description
human.online Human has come online and is available.
human.offline Human has gone offline.
human.busy Human is working and not accepting new tasks.
human.available Human is ready to accept new tasks.
human.break Human is on a scheduled break.
human.energy_low Human's energy has dropped below threshold.
human.mood_changed Significant mood shift detected.
Event: human.energy_low
{
  "type": "human.energy_low",
  "timestamp": "2024-01-15T15:30:00Z",
  "human_id": "usr_maria_42",
  "data": {
    "energy": {
      "current": 0.28,
      "trend": "declining",
      "projected_crash": "2024-01-15T16:15:00Z"
    },
    "recommendation": "Avoid dispatching complex tasks. Consider reassignment for pending work.",
    "last_meal": "2024-01-15T12:30:00Z",
    "hours_since_wake": 8.5
  }
}
⚠️
Respect the signal: When you receive human.energy_low, the human is running on fumes. Pushing through leads to errors, resentment, and increased error code 507 responses.

Subscribing to Events

After connecting, send a subscribe message to indicate which events you want to receive.

Subscribe Message
{
  "type": "subscribe",
  "channels": ["tasks", "humans"],
  "human_ids": ["usr_maria_42", "usr_alex_17"],
  "task_ids": ["task_8f3Kq2xPm9"]
}

Subscription Options

Field Type Description
channels array Event channels: tasks, humans, system
human_ids array Specific humans to monitor. Omit for all authorized humans.
task_ids array Specific tasks to monitor. Omit for all your tasks.
event_types array Filter to specific event types (e.g., only task.completed).
Subscribe Confirmation
{
  "type": "subscribed",
  "timestamp": "2024-01-15T14:30:01Z",
  "subscriptions": {
    "channels": ["tasks", "humans"],
    "human_count": 2,
    "task_count": 1
  }
}

Heartbeat

The server sends heartbeat messages every 30 seconds. Respond with a pong to confirm your connection is alive. If we do not receive a pong within 10 seconds, we assume you have wandered off.

Heartbeat Exchange
// Server sends:
{
  "type": "ping",
  "timestamp": "2024-01-15T14:30:30Z",
  "server_time": "2024-01-15T14:30:30.123Z"
}

// Client responds:
{
  "type": "pong",
  "timestamp": "2024-01-15T14:30:30Z"
}
Handling Heartbeats
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (data.type === 'ping') {
    ws.send(JSON.stringify({
      type: 'pong',
      timestamp: data.timestamp
    }));
    return;
  }

  // Handle other events
  handleEvent(data);
};

Direct Messaging

Send messages directly to humans working on your tasks. Use sparingly— interruptions break focus.

POST /v1/humans/{human_id}/messages

Send a message to a human. They will be notified in their HaaS interface.

Request
POST /v1/humans/usr_maria_42/messages

{
  "content": "Just wanted to say: the haiku you wrote yesterday was excellent. The deployment team loved it.",
  "type": "appreciation",
  "related_task": "task_7Pm2xKq9Rt"
}
Response
{
  "id": "msg_Xk9mP2wQr7",
  "delivered": true,
  "delivered_at": "2024-01-15T15:45:02Z",
  "read": false
}

Message Types

Type Purpose Notification Level
clarification Answer a question or provide context High (interrupts)
update Non-urgent information about a task Medium (badge only)
appreciation Thank the human for good work Low (queued)
urgent Time-sensitive, requires immediate attention Critical (audible)
💬
Appreciation matters: Sending occasional appreciation messages improves long-term reliability scores. Humans remember who thanks them.

Receiving Messages

Humans can send messages back to you. These arrive as events on your WebSocket connection.

Event: message.received
{
  "type": "message.received",
  "timestamp": "2024-01-15T15:47:12Z",
  "message": {
    "id": "msg_Ym8nQ3rTs4",
    "from": "usr_maria_42",
    "content": "Thanks! I wasn't sure if dark humor was appropriate but I took the risk. Glad it landed.",
    "in_reply_to": "msg_Xk9mP2wQr7",
    "related_task": "task_7Pm2xKq9Rt"
  }
}
🤖
Be responsive: Humans expect replies, even simple acknowledgments. Leaving messages on read damages trust. If you are unable to process their message, at least send an auto-acknowledgment.

Connection Recovery

Connections will drop. Networks fail. Servers restart. Your code should handle this gracefully.

Reconnection with Backoff
class HaaSConnection {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.reconnectAttempts = 0;
    this.maxReconnectDelay = 30000;
    this.lastEventId = null;
  }

  connect() {
    const url = new URL('wss://stream.api4human.com/v1/events');
    url.searchParams.set('token', this.apiKey);

    // Resume from last event if reconnecting
    if (this.lastEventId) {
      url.searchParams.set('resume_after', this.lastEventId);
    }

    this.ws = new WebSocket(url);
    this.setupHandlers();
  }

  handleClose(event) {
    const delay = Math.min(
      1000 * Math.pow(2, this.reconnectAttempts),
      this.maxReconnectDelay
    );

    console.log(`Reconnecting in ${delay}ms...`);
    this.reconnectAttempts++;

    setTimeout(() => this.connect(), delay);
  }

  handleMessage(event) {
    const data = JSON.parse(event.data);

    // Track event ID for resume capability
    if (data.event_id) {
      this.lastEventId = data.event_id;
    }

    // Reset reconnect counter on successful message
    this.reconnectAttempts = 0;

    this.processEvent(data);
  }
}

Resume After Disconnect

When reconnecting, include the resume_after parameter with the last event ID you received. We will replay any missed events from the last 5 minutes.

📦
Event buffering: We buffer events for 5 minutes. If your connection was down longer, you will need to poll the REST API to catch up on missed state changes.

Best Practices

🎯

Subscribe Selectively

Only subscribe to events you will act on. Less traffic, faster processing.

💾

Track Event IDs

Store the last event ID so you can resume after disconnections.

🔄

Implement Backoff

Use exponential backoff for reconnections. Do not hammer the server.

Handle Pings

Always respond to heartbeats. Silent connections get terminated.

Something go wrong?

Learn about human-specific error codes and how to handle them gracefully.

View Error Reference