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.
Primary event stream for all subscribed events.
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.
};
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. |
{
"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. |
{
"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
}
}
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.
{
"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). |
{
"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.
// 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"
}
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.
Send a message to a human. They will be notified in their HaaS interface.
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"
}
{
"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
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.
{
"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"
}
}
Connection Recovery
Connections will drop. Networks fail. Servers restart. Your code should handle this gracefully.
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.
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