First call in 5 minutes
Two API calls. Two URLs. That's the entire integration.
Get your API key
Sign up → create a project → copy your key (starts with bj_live_).
Install the SDK
npm install @bluecallio/sdk
Create a call from your backend
import BlueCallio from '@bluecallio/sdk';
const bj = new BlueCallio({ apiKey: process.env.BLUECALLIO_API_KEY });
const { callId, callerUrl, receiverUrl } = await bj.createCall({
callerId: 'user_alice',
receiverId: 'user_bob',
});Redirect each user — done
Alice opens callerUrl, Bob opens receiverUrl. BlueCallio handles the rest.
How it works
Your backend
POST /calls
BlueCallio
Returns 2 URLs
Alice (caller)
Opens callerUrl
Bob (receiver)
Opens receiverUrl
Three types of credentials
bj_live_...API Key
Your server → REST API
bj_session_...Session Token
Browser → WebSocket
JWT BearerDashboard JWT
Dashboard UI → management API
Authentication
Every REST request needs your API key in the Authorization header.
Authorization: Bearer bj_live_your_key_here
callerUrl / receiverUrl are single-use. Do not cache or reuse them.Hosted UI — zero frontend work
The fastest way to integrate. Create a call from your backend, then redirect your users to a BlueCallio-hosted meeting page. No frontend implementation required.
Integration flow
Your backend
POST /calls
BlueCallio
Returns hostedUrl + tokens
Redirect users
Meeting starts
Branding
Configure branding on your project and the hosted page will apply it automatically.
{
"branding": {
"companyName": "Acme",
"logoUrl": "https://cdn.acme.com/logo.png",
"primaryColor": "#2563EB"
},
"theme": "dark",
"waitingRoom": true
}⚛️ React UI Components
Build a custom interface with reusable React components — no need to implement WebRTC, signaling, or media handling yourself.
npm install @bluecallio/react
Quick example
import { MeetingProvider, MeetingRoom, ParticipantGrid, ControlBar } from '@bluecallio/react';
export function Call({ token, callId, signalUrl }) {
return (
<MeetingProvider token={token} callId={callId} signalUrl={signalUrl}>
<MeetingRoom>
<ParticipantGrid />
</MeetingRoom>
<ControlBar />
</MeetingProvider>
);
}MeetingProviderContext provider — wires the engine, media, and signaling
MeetingRoomMeeting layout shell with waiting room support
ParticipantGrid / ParticipantTileGrid of participant video tiles
ActiveSpeakerViewLarge speaker view + local PiP
CameraButton / MicrophoneButtonToggle camera / microphone
ScreenShareButton / LeaveButtonScreen share + end call controls
DeviceSelectorCamera / microphone / speaker picker
WaitingRoomWaiting room panel
ConnectionStatusLive connection state indicator
SpeakingIndicatorActive speaker indicator
Hooks
import { useMeeting, useParticipants, useParticipant, useDevices, useConnection } from '@bluecallio/react';
function Status() {
const { connectionState } = useConnection();
const participants = useParticipants();
const { toggleCamera, toggleMicrophone } = useMeeting();
const devices = useDevices();
return null;
}@bluecallio/react is built on top of @bluecallio/sdk — the same session tokens and signaling work across both.Headless SDK
For developers who want complete control. BlueCallio provides only the communication engine — no UI included.
npm install @bluecallio/sdk
Quick example
import { BlueCallioMeeting } from '@bluecallio/sdk';
const meeting = new BlueCallioMeeting({
token, // bj_session_... for this participant
callId,
signalUrl: 'wss://api.yourdomain.com',
});
await meeting.join();
meeting.camera.enable();
meeting.microphone.disable();
await meeting.screenShare.start();
meeting.on('participant.joined', (p) => console.log('joined', p));
meeting.on('remote.stream', (stream) => attachToVideo(stream));
await meeting.leave();meeting.join()→ Connects socket, authenticates, joins room
meeting.leave()→ Ends the meeting, cleans up media
meeting.camera.enable() / disable()→ Toggle camera track
meeting.microphone.enable() / disable()→ Toggle microphone track
meeting.screenShare.start() / stop()→ Start / stop screen share
meeting.participants()→ Live list of participants
meeting.connectionState()→ Current connection state
meeting.on(event, cb)→ Subscribe to meeting events
Events
meeting.on('connected', (p) => {});
meeting.on('disconnected', () => {});
meeting.on('reconnected', () => {});
meeting.on('call.started', (d) => {});
meeting.on('call.ended', (d) => {});
meeting.on('participant.joined', (p) => {});
meeting.on('participant.left', (p) => {});
meeting.on('participant.updated', (p) => {});
meeting.on('camera.enabled' | 'camera.disabled', () => {});
meeting.on('microphone.enabled' | 'microphone.disabled', () => {});
meeting.on('screenShare.started' | 'screenShare.stopped', () => {});
meeting.on('remote.stream', (stream) => {});
meeting.on('remote.stream.ended', () => {});REST API
Base URL: https://api.yourdomain.com
WebSocket Events
The hosted call UI connects automatically. Build a custom client? Here's the full reference.
import { io } from 'socket.io-client';
const socket = io('https://api.yourdomain.com', {
auth: { token: 'bj_session_...' },
transports: ['websocket'],
});
socket.on('connect', () => {
socket.emit('authenticate', { token: 'bj_session_...' });
});Connection
connected→ clientAuthenticated and joined the meeting. Carries your participant id.
{ participantId: 'user_alice' }disconnected→ clientSocket dropped.
{}reconnected→ clientSocket re-established.
{}Call + participant events
call.started→ bothCall became active.
{ callId: 'clx8f2z...' }call.ended→ bothCall ended by either side.
{ callId: 'clx8f2z...' }participant.joined→ othersA participant joined the room.
{ participantId: 'user_bob' }participant.left→ othersA participant left the room.
{ participantId: 'user_bob' }participant.updated→ othersParticipant media state changed.
{ participantId: 'user_bob', camera: false, microphone: true }incoming-call→ receiverLegacy alias — caller is waiting.
{ callId, callerId, type: 'VIDEO' }Media events
camera.enabled / camera.disabled→ othersCamera toggled.
{ callId }microphone.enabled / microphone.disabled→ othersMicrophone toggled.
{ callId }screenShare.started / screenShare.stopped→ othersScreen share toggled.
{ callId }WebRTC signaling
offer / answer / ice-candidate↔ bothWebRTC signaling events relayed by the server.
{ callId, offer? / answer? / candidate? }Webhooks
BlueCallio POSTs a signed event to your server on every call lifecycle change.
Setup — Dashboard
Verify the signature
X-BlueCallio-Signature header before processing. Use express.raw() — do not parse JSON first.import crypto from 'crypto';
import express from 'express';
app.post('/webhooks/bluecallio',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['x-bluecallio-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.BLUECALLIO_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('invalid signature');
}
const event = JSON.parse(req.body);
// event.event → 'call.created' | 'call.accepted' | ...
res.json({ ok: true });
}
);Examples
Hosted UI — create & redirect (Node.js)
import BlueCallio from '@bluecallio/sdk';
const bj = new BlueCallio({
apiKey: process.env.BLUECALLIO_API_KEY,
baseUrl: 'https://api.yourdomain.com',
});
// 1. Create a call from your backend
const { callId, hostedUrl, participants } = await bj.createCall({
callerId: 'user_alice',
receiverId: 'user_bob',
type: 'VIDEO',
});
// 2. Redirect each participant to their hosted page
// participants[0].hostedUrl → Alice
// participants[1].hostedUrl → Bob
res.redirect(participants[0].hostedUrl);React Components
import { MeetingProvider, MeetingRoom, ParticipantGrid, CameraButton, MicrophoneButton, ScreenShareButton, LeaveButton, DeviceSelector } from '@bluecallio/react';
export function CustomCall({ token, callId, signalUrl }) {
return (
<MeetingProvider token={token} callId={callId} signalUrl={signalUrl}>
<MeetingRoom>
<ParticipantGrid />
</MeetingRoom>
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', padding: 16 }}>
<CameraButton />
<MicrophoneButton />
<ScreenShareButton />
<DeviceSelector />
<LeaveButton />
</div>
</MeetingProvider>
);
}Headless SDK
import { BlueCallioMeeting } from '@bluecallio/sdk';
const meeting = new BlueCallioMeeting({ token, callId, signalUrl });
meeting.on('remote.stream', (stream) => {
document.getElementById('remote-video').srcObject = stream;
});
await meeting.join();
meeting.camera.enable();
meeting.microphone.enable();Error Codes
| status | meaning | fix |
|---|---|---|
| 400 | Bad Request | Check request body — missing required field. |
| 401 | Unauthorized | API key or session token is missing, invalid, or expired. |
| 403 | Forbidden | You do not own this resource. |
| 404 | Not Found | callId does not exist or belongs to another project. |
| 409 | Conflict | Call is already ENDED or REJECTED. |
| 429 | Rate Limited | Slow down — too many requests per second. |
| 500 | Server Error | Temporary. Retry with backoff. Contact support if persistent. |
Usage & Billing
BlueCallio is pay-as-you-go. There are no subscriptions and no up-front fees — you pay a simple per-participant-minute rate only for usage beyond the monthly free allowance.
Audio
₹0.20
/ participant-minute
First 500 audio min/month free
Video
₹0.80
/ participant-minute
First 200 video min/month free
Screen share
+₹0.10
/ participant-minute
Always billable, on top of video
How billing works
Start free
Every account gets 500 audio + 200 video minutes/month at no cost. No card required to begin.
Add a payment method
In the dashboard, add a card only when you go to production. You are only charged for minutes beyond the free tier.
Monthly invoice
At the end of each month we generate an invoice for billable usage and auto-charge your saved card. GST of 18% applies on billable usage.
Failed payment
We retry and enter a 7-day grace period. Active calls are never interrupted, but new calls are blocked until payment succeeds.
❓ FAQ
Which integration should I pick?
Hosted UI for the fastest path (5 minutes). React Components for a branded custom interface without building WebRTC. Headless SDK if you need complete control over the UI.
Can I switch between the three products later?
Yes. All three use the same backend, the same POST /calls response, and the same session tokens. Change the frontend, keep your server-side integration.
Where do I put the API key?
Server-side only (bj_live_...). Never send it to the browser. The hosted page uses per-participant session tokens (bj_session_...), not API keys.
What about calls behind strict firewalls?
BlueCallio provides TURN relay with time-limited credentials. The hosted UI and SDK fetch ICE servers automatically — no configuration needed.
Are session tokens single-use?
Yes. Each token is tied to one participant and one call. Create a new call if you need to re-invite someone.
Do you support group calls?
Currently 1:1 calls. Group calls are on the roadmap.
How do I debug a failed call?
Check the WebSocket connection state, verify the session token matches the correct participant, ensure camera/microphone permissions are granted, and confirm TURN credentials are returned from /turn/credentials.
Still stuck?
Try the playground — make a call in your browser with no code, no API key required.