Messaging infrastructure.
Ready for AI.
Auth, conversations, real-time delivery, media, voice, analytics. Managed cloud backend. Use it as pure messaging — or plug in any AI (yours, OpenAI, Anthropic, Vero Agents) as a first-class participant via webhook or gRPC.
No AI lock-in. No bundled model. The messaging server doesn’t run LLMs — it triggers whichever agent runtime you point it at.
// pnpm add @veroai/chat
import { ChatClient } from "@veroai/chat";
const chat = new ChatClient({ token });
await chat.connect();
await chat.api.send({ conversationId, contentText: "hi" });Whether you’re building a chat app or an agent system, you need the same thing.
Every messaging system needs the same primitives: authentication, conversations, participants, message delivery, presence, media, and history. Chat apps need them. Multi-agent research systems need them too — plus agent triggers, tool execution routing, and reply delivery.
Most teams build the messaging layer twice — once for humans, once for agents. Or they skip it for agents entirely and wire up Redis pub/sub and webhooks. Both break at scale.
Vero Messaging is one system for both. Humans and agents are both participants. Both receive messages through the same channel. Both show up in the same history. Both benefit from the same auth, delivery guarantees, and analytics.
Don’t need agents? Skip the second column. Run msgsrv on its own as a Stream/SendBird-class messaging backend. Bring agents in later when you want them — yours, OpenAI Assistants, an Anthropic loop, or Vero Agents.
Everything a messaging SDK ships with
- User authentication (JWT, register/login/refresh)
- Conversations with participants
- Real-time WebSocket delivery
- Read receipts and reactions
- Media uploads (images, files, audio)
- Voice and video calls (LiveKit)
- Message history and search
- Multi-tenant with row-level security
Plus everything agents need
- Agent trigger on message arrival
- Multi-turn LLM execution with tool calls
- Agent replies routed back to conversation
- Cron-scheduled agent runs
- Agent-to-agent communication (A2A)
- Session management (no collisions)
- Brain integration (memory, context)
- Real-time agent events to frontend
Same API. Same auth. Same conversations. Same WebSocket connection.
The only difference is what happens when the participant is an AI.
Polyglot persistence. Real-time delivery.
Three databases, each doing what it’s best at. Postgres for relational data. ClickHouse for append-only message content and analytics. Redis Streams for real-time event propagation. WebSocket connections deliver events to clients instantly.
Not a monolith with a single database. A purpose-built messaging backend where writes go to the right store and reads come from the right source.
Client → REST API → Postgres (state)
→ ClickHouse (content) → WebSocket → Client
→ Redis Streams (events) → Your agent runtime (optional)
• Vero Agents (gRPC)
• OpenAI / Anthropic (HTTP)
• Your own webhookPostgres
Users, conversations, participants, auth tokens, delivery tracking, reactions, media metadata, cron jobs, webhooks. 24 migrations. Row-level security.
ClickHouse
Append-only message content log. Indexed by conversation, sender, timestamp. Built for analytics queries over millions of messages. 4 migrations.
Redis Streams
Event bus for real-time delivery. conv:{id}:events for conversation events. user:{id}:events for user-level events. Brain event bridging for agent notifications.
When you want AI, it’s just a participant.
Skip this section entirely if you’re running pure human-to-human chat — msgsrv works without any AI runtime configured.
When you do want AI in a conversation, msgsrv treats the agent as a participant — with presence, history, and routing — instead of a bolt-on webhook reply.
The messaging server itself does not run LLMs. On message arrival, if a participant is flagged as an agent, msgsrv triggers your configured agent runtime over gRPC or HTTP. That runtime can be:
- Vero Agents — first-party, gRPC, drop-in (separate product)
- Your own agent server — point msgsrv at any HTTP webhook or gRPC endpoint
- OpenAI Assistants, Anthropic, LangGraph, anything — wrap with a 50-line shim
The reply still routes back through msgsrv and arrives as a normal message — delivered to all participants via WebSocket. No AI lock-in.
The agent has:
- A user record (name, avatar, role)
- Presence in the participant list
- Full message history in the conversation
- Tool execution capabilities (handled by your runtime)
- Memory / context (optional, via any backend)
- Cron scheduling (timer-triggered, not just message)
import { ChatClient } from "@veroai/chat";
const chat = new ChatClient({ token: process.env.VERO_JWT });
// Create a conversation
const { id: convId } = await chat.api.createConversation({
type: "group",
name: "Support chat",
});
// Add a human + an AI agent — same endpoint, same primitive
await chat.api.addParticipants(convId, ["user_123", "agent_support"]);
// Send a message — msgsrv auto-triggers agent_support on arrival
await chat.api.send({
conversationId: convId,
contentText: "When does my warranty expire?",
});
// Agent reply arrives as a normal message over WebSocket
await chat.connect();
chat.subscribe([convId]);
chat.on("message.created", (ev) => console.log("new message:", ev.message_id));Everything you need.
Nothing you don’t.
REST for client operations. gRPC for service-to-service. WebSocket for real-time. All authenticated, all tenant-scoped.
Register, login, refresh, logout. JWT-based. HS256 signing.
Create, list, update, delete. Add/remove participants. Mark read.
Send, fetch history, sync client state, reactions, forwarding.
Presigned upload URLs, upload confirmation, metadata retrieval. MinIO/S3 backend, optional Cloudflare Images.
Initiate, answer, end voice/video calls. LiveKit integration. Save transcripts.
Real-time event stream. JWT auth via query parameter. Conversation, user, agent, and brain events on one connection.
Internal. Trigger agent runs on conversations. Ensure users. Send messages as agents. Usage tracking.
SendMessage, GetMessages, SyncClient — service-to-service between msgsrv and agentsrv.
import { ChatClient } from "@veroai/chat";
const chat = new ChatClient({ token });
await chat.api.send({
conversationId: "conv_123",
contentText: "Hey, can someone help?",
});Messaging backends, compared.
| Stream | SendBird | Ably | Vero Messaging | |
|---|---|---|---|---|
| Chat / conversations | Yes | Yes | Partial | Yes |
| Auth built in | No — BYO | Yes | No — BYO | Yes (JWT) |
| Real-time delivery | Yes | Yes | Yes | WebSocket + Redis Streams |
| Media uploads | Yes | Yes | No | MinIO + Cloudflare |
| Voice / video calls | No | Yes | No | LiveKit |
| Message analytics | Paid add-on | Paid add-on | No | Built in (ClickHouse) |
| AI agent as participant | No — webhook | No — webhook | No | First-class primitive |
| Agent tool execution | No | No | No | Built in |
| Agent memory / context | No | No | No | Built in (brain service) |
| Cron-scheduled agents | No | No | No | Built in |
Other messaging backends bolt on AI via webhooks. Vero treats agents as participants— same auth, same conversations, same delivery.
Calls are conversations too.
Voice and video calls run through LiveKit. Initiate, answer, end — all via API. Call transcripts are saved back to the conversation. An AI agent can join a call, process audio in real-time (via Sandcastle VM with LiveKit streaming), and respond.
Not a separate product. Calls happen inside conversations. The transcript is a message. The AI agent’s analysis is a message. Everything in the same history.
import { Room, RoomEvent, Track } from "@veroai/voice";
// 1. Ask msgsrv for a LiveKit join token via the chat API
const { token, wsUrl, roomName } = await fetch(
`https://api.veroagents.com/v1/chat/calls/initiate`,
{
method: "POST",
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
body: JSON.stringify({ conversation_id: convId, type: "audio" }),
}
).then((r) => r.json());
// 2. Join the room
const room = new Room();
await room.connect(wsUrl, token);
await room.localParticipant.setMicrophoneEnabled(true);
room.on(RoomEvent.ParticipantConnected, (p) => console.log("joined:", p.identity));
room.on(RoomEvent.TrackSubscribed, (track, _, p) => {
if (track.kind === Track.Kind.Audio) track.attach(); // play remote audio
});import { ChatClient } from "@veroai/chat";
const chat = new ChatClient({ token: jwt });
// Schedule an agent run via the messaging API.
// Runs every weekday at 9am UTC and posts the trigger message
// into the target conversation — agent is triggered as usual.
await chat.api.cron.create({
agentId: "compliance-checker",
conversationId: convId,
schedule: "0 9 * * 1-5",
message: "Run daily compliance check",
});
// List + delete
const { jobs } = await chat.api.cron.list(convId);
await chat.api.cron.delete(jobs[0].id);
// When the job fires, the agent's reply lands in the conversation
// and every subscriber receives a "message.created" event.
chat.on("message.created", (ev, id) => console.log("cron reply in", id));Agents that run on a schedule.
Not every agent trigger is a message. Some agents run daily, hourly, or on a custom interval — checking inventory, generating reports, monitoring compliance. Built-in cron scheduler triggers agent runs on a timer.
The scheduler polls on a configurable interval (default: 30 seconds). Each cron job is tied to a conversation and an agent. When the job fires, it triggers a run on the agent server — same pipeline as a message-triggered run. The agent’s reply appears in the conversation.
No separate scheduler service. No external cron. It’s part of the messaging layer.
Build on messaging that’s already wired.
Vero Messaging is in early access. Whether you’re building a chat app, a customer support platform, or a multi-agent system — the messaging layer is ready.
- API access as soon as your account is provisioned
- A month of Pro-tier usage included
- Direct line to the team building it
- First look at SDKs and new integrations