Persistent bidirectional connection for real-time messaging, typing indicators, and streaming AI responses. Pair with the REST API for fully interactive experiences.
Connect to the endpoint below. Authenticate via query parameter or by sending an auth message as the first frame.
wss://api.dyva.ai/v1/wsOption 1 — Query parameter: Append your token to the URL.
wss://api.dyva.ai/v1/ws?token=YOUR_ACCESS_TOKENOption 2 — Auth message: Connect without a token, then authenticate with the first message.
// Alternative: authenticate via the first message instead of query param
const ws = new WebSocket("wss://api.dyva.ai/v1/ws");
ws.addEventListener("open", () => {
ws.send(JSON.stringify({ type: "auth", token: "YOUR_ACCESS_TOKEN" }));
});All messages are JSON-encoded text frames.
{
"type": "message",
"payload": {
"conversation_id": "conv_abc123",
"content": "Hello!"
}
}{
"type": "message",
"payload": {
"role": "assistant",
"content": "Hey there! How can I help?",
"created_at": "2026-03-09T12:00:00Z"
},
"error": null
}Events you send to the server.
| Type | Description |
|---|---|
subscribe | Start receiving updates for a conversation. |
unsubscribe | Stop receiving updates for a conversation. |
message | Send a message to a conversation. |
typing | Signal typing status to other participants. |
ping | Keep the connection alive. No payload required. |
Events you receive from the server.
| Type | Description |
|---|---|
message | New message in a subscribed conversation. Includes role, content, and timestamp. |
typing | Another participant is typing. |
stream_start | AI response generation has begun. |
stream_delta | Incremental chunk of the AI response. Payload contains a content delta string. |
stream_end | AI response complete. Payload contains the full message object. |
error | Something failed. Payload includes an error code and message. |
pong | Response to a client ping. No payload. |
Dyva responses arrive as three sequential events: stream_start, one or more stream_delta chunks, and a final stream_end with the complete message. Concatenate deltas for a live typing effect.
// stream_start
{"type": "stream_start", "payload": {"conversation_id": "conv_abc123"}}
// stream_delta (repeated)
{"type": "stream_delta", "payload": {"content": "Sure, "}}
{"type": "stream_delta", "payload": {"content": "I can "}}
{"type": "stream_delta", "payload": {"content": "help with that!"}}
// stream_end
{"type": "stream_end", "payload": {"role": "assistant", "content": "Sure, I can help with that!", "created_at": "2026-03-09T12:00:01Z"}}Connect, subscribe, send a message, and handle incoming events.
const ws = new WebSocket("wss://api.dyva.ai/v1/ws?token=YOUR_ACCESS_TOKEN");
ws.addEventListener("open", () => {
console.log("Connected");
// Subscribe to a conversation
ws.send(JSON.stringify({
type: "subscribe",
payload: { conversation_id: "conv_abc123" },
}));
});
ws.addEventListener("message", (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case "message":
console.log(`[${msg.payload.role}] ${msg.payload.content}`);
break;
case "stream_delta":
process.stdout.write(msg.payload.content);
break;
case "stream_end":
console.log("\nAI response complete:", msg.payload.content);
break;
case "error":
console.error("Error:", msg.payload.message);
break;
case "pong":
break;
}
});
// Send a message
ws.send(JSON.stringify({
type: "message",
payload: {
conversation_id: "conv_abc123",
content: "Hello, what can you help me with?",
},
}));
// Keep alive — send a ping every 30 seconds
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 30_000);Idle connections close after 5 minutes without a ping. Send pings every 30 seconds. On disconnect, reconnect with exponential backoff.
function connectWithBackoff(url, maxRetries = 10) {
let attempt = 0;
function connect() {
const ws = new WebSocket(url);
ws.addEventListener("open", () => {
console.log("Connected");
attempt = 0; // reset on success
});
ws.addEventListener("close", (event) => {
if (attempt < maxRetries) {
const delay = Math.min(1000 * 2 ** attempt, 30_000);
console.log(`Reconnecting in ${delay}ms (attempt ${attempt + 1})`);
setTimeout(connect, delay);
attempt++;
}
});
return ws;
}
return connect();
}
const ws = connectWithBackoff(
"wss://api.dyva.ai/v1/ws?token=YOUR_ACCESS_TOKEN"
);On failure, the server sends an error event. Error codes:
| Code | Meaning |
|---|---|
auth_failed | Invalid or expired token. Refresh and reconnect. |
not_subscribed | Message sent to an unsubscribed conversation. |
conversation_not_found | Conversation does not exist or is inaccessible. |
rate_limited | Too many messages. Back off and retry. |
internal_error | HardDrives-side failure. Retry with backoff. |
// Example error event
{
"type": "error",
"payload": {
"code": "not_subscribed",
"message": "You must subscribe to conversation conv_abc123 before sending messages."
},
"error": true
}