Build with OpenVibe Chat
One chat system, from a private chat with Vibe to a chat bubble on your website. Everything here is open: a tag for any site, a REST API and socket, the openvibe-sdk/chat package, and signed webhooks. Free to use; open source.
Chat on your site
A support inbox: make one at openvibe.chat/inbox, then add one line before </body>. Vibe answers first from your instructions and pages; visitors can ask for a person, and your team answers from the inbox (or turn people off for a site assistant).
<script src="https://openvibe.chat/widget.js" data-inbox="inb_…" async></script>
A public room, as a bubble or inside your page. Anyone reads; people sign in with OpenVibe to write, and the room's moderation applies.
<script src="https://openvibe.chat/widget.js" data-room="your-room" async></script>
<script src="https://openvibe.chat/widget.js" data-room="your-room" data-target="#chat" data-height="520" async></script>
Options: data-position="left", data-color="#ff7a1a", data-open="1". From your page: OpenVibeChat.open(), .close(), .toggle(), .identify(user); it says what happens with the events openvibechat:open, openvibechat:close and openvibechat:message on window. The panel runs on openvibe.chat's own origin, in a frame your site's CSS cannot reach; an inbox's allowed sites decide who may frame it.
Verified visitors
When your site has its own accounts, turn on verified visitors in the inbox's settings and keep its secret on your server. For a signed-in visitor, sign their id; your team then sees their verified name and email, and their conversation follows them to every device.
// Node
const hash = crypto.createHmac('sha256', SECRET).update(String(user.id)).digest('hex');
// in the page
<script src="https://openvibe.chat/widget.js" data-inbox="inb_…" data-user-id="42" data-user-name="Dana Lee"
data-user-email="[email protected]" data-user-hash="…" async></script>
Your own panel
The visitor API answers any site with no cookies: the visitor's token is the key. Build your own chat UI on it, in the browser or on your server.
import { createClient } from 'openvibe-sdk';
import { createInboxVisitor } from 'openvibe-sdk/chat';
const visitor = createInboxVisitor(createClient(), { inbox: 'inb_…', token: localStorage.getItem('chat') });
const { token, conversation, messages } = await visitor.visit(); // keep the token: it is the visitor
localStorage.setItem('chat', token);
await visitor.send('Where is my order?', { page_url: location.href });
const { messages: next } = await visitor.messages({ after: lastId });
await visitor.handoff(); // "Talk to a person"
await visitor.rate(1, { comment: 'Quick!' });
Over HTTP: GET https://openvibe.chat/api/chat/widget/inbox/:id, POST …/visit, GET|POST …/messages, POST …/handoff, POST …/rating.
Feeds from Claude Code (or anything)
Show what you are building, live: in a room, on your OpenVibe Live channel while you stream, or on your own site. Make a feed at openvibe.chat/feeds; it gives you a key and this block for .claude/settings.json, Claude Code's own http hooks (no extension). Secrets and home folders are wiped before anyone sees a step.
{ "hooks": { "UserPromptSubmit": [{ "hooks": [{ "type": "http",
"url": "https://openvibe.chat/api/chat/feeds/fed_…/hooks/claude-code",
"headers": { "Authorization": "Bearer $OPENVIBE_FEED_KEY" }, "allowedEnvVars": ["OPENVIBE_FEED_KEY"] }] }],
"PostToolUse": […the same…], "Stop": […], "SessionStart": […], "SessionEnd": […] } }
From anything else, with the same key:
import { createFeedPublisher } from 'openvibe-sdk/chat';
const feed = createFeedPublisher(createClient(), { feed: 'fed_…', key });
await feed.publish({ eventType: 'prompt', text: 'Add a dark mode' });
await feed.publish([{ eventId: 'e1', eventType: 'tool.call', summary: 'Ran the tests', tool: { name: 'Bash' } }]);
On any page: <iframe src="https://openvibe.chat/embed/feed/fed_…" style="width:100%;height:320px;border:0"></iframe>
The SDK: act as a person or a bot
With a person's sign-in or an OpenVibe API token, createChatClient covers global chat, rooms, direct messages, Vibe, inboxes (conversations, replies, saved replies, verified visitors, webhooks) and feeds.
npm install openvibe-sdk
import { createClient } from 'openvibe-sdk';
import { createChatClient } from 'openvibe-sdk/chat';
const chat = createChatClient(createClient({ token: process.env.OPENVIBE_TOKEN }));
const { conversations } = await chat.inboxes.conversations('inb_…', { status: 'waiting' });
await chat.inboxes.reply('inb_…', conversations[0].id, 'Hi! I can help with that.');
await chat.rooms.send('builders', 'Shipping now');
Live delivery is the chat socket, wss://openvibe.chat/ws/chat: join_room, inbox_watch and feed_watch.
Webhooks
An inbox's owner adds up to three https addresses. A Slack or Discord webhook gets a line when a visitor waits for a person, writes while waiting, or rates a conversation. Any other address gets every event as JSON, signed like OpenVibe.Events deliveries: chat.inbox.conversation.created, .waiting, .resolved, .rated and chat.inbox.message.created.
import { parseDelivery } from 'openvibe-sdk/events';
app.post('/hooks/openvibe', express.raw({ type: 'application/json' }), (req, res) => {
const d = parseDelivery(req.body, req.headers, process.env.WEBHOOK_SECRET, { requireV2: true });
if (!d) return res.sendStatus(401);
if (d.event.type === 'chat.inbox.conversation.waiting') page(d.event.data.conversation);
res.sendStatus(204);
});
Limits
| What | Limit |
|---|---|
| Visitor writes (one address) | 20 a minute |
| Vibe's answers in an inbox | 30 a conversation, 500 an inbox a day |
| Inboxes, people, saved replies, webhooks | 10 a person; 50 people, 100 saved replies and 3 webhooks an inbox |
| Feed events | 600 a minute a feed (a batch of 50 is one request); a feed keeps its newest 5,000 |
| Feeds | 10 a person |
The full reference is Chat's README on GitHub (support inboxes, feeds); the whole platform's building blocks are on openvibe.services.