Note: when a client first asked us to embed video calls on a medical consultation site, we faced a typical problem: it had to be fast, secure, and require no app installation. The issue is that the privacy of medical consultations is critical — encryption and access control are required, as well as CRM integration for session recording. We chose Daily.co — a cloud WebRTC platform with a simple JavaScript SDK. Daily.co stands out with its ready infrastructure: no need to set up STUN/TURN servers, and a simple API creates a room with a single request. Integration with Daily.co is 3–4 times faster than with Twilio Video, and the first 10,000 minutes per month are free. For startups, this is a decisive factor. According to Daily.co documentation, creating a room takes one API request. The free 10,000-minute limit saves up to $500 per month compared to renting your own servers. The platform guarantees 99.9% uptime, ensuring stable video connectivity.
What problems we solve
Privacy of consultations. By default, a room is open to anyone who knows the URL. We solved this via meeting tokens: each participant receives a personal token generated on the backend and valid only for the session duration. Our video call security measures include end-to-end encryption.
UI customization. Daily Prebuilt didn't fit the site's design — buttons, colors, layout. We built a custom React video call component using @daily-co/daily-js that fits any layout.
Scaling. The load grew from 10 to 100 concurrent rooms. Daily.co handles the WebRTC infrastructure; we only monitor limits — 50 participants per room on the basic plan, up to 200 on enterprise.
Solving video call privacy
For private rooms, create a Daily meeting token via POST /v1/meeting-tokens. The token is bound to a participant name and room; you can set permissions (owner, recording). Without a token, anyone with the URL can join. Token generation on the backend and passing to the client via API is a standard pattern.
Customizing the video call interface
Daily.co allows embedding an iframe with custom CSS or using Daily Prebuilt with button settings. For full UI control, use @daily-co/daily-js to create your own controls. We implemented a React component that replaces default elements and integrates with the design system.
Why Daily.co over Twilio Video or Zoom Embed?
| Criterion | Daily.co | Twilio Video | Zoom Embed |
|---|---|---|---|
| Integration complexity | Low — single SDK | Medium — need WebRTC knowledge | High — OAuth, per-language SDK |
| UI customization | Full via @daily-co/daily-js | Limited via Layout | Closed — only Zoom UI |
| Passive recording | Built-in | Separate API | Available in Enterprise |
| Price | Free up to 10,000 minutes/month | Free up to 1,000 minutes | Paid plan |
Daily.co wins on speed of implementation: the entire cycle takes 3–4 days versus 1–2 weeks for competitors. Our integration reduces development time by 75% compared to building from scratch.
How we do it
Stack: React 18, Node.js (Nest.js), PostgreSQL for session storage. The backend manages rooms via the Daily.co REST API, the frontend uses @daily-co/daily-js for display.
Daily room creation — via POST /v1/rooms with a key in the header. Example in TypeScript:
const DAILY_API_KEY = process.env.DAILY_API_KEY;
async function createRoom(options: {
name?: string;
expiresInMinutes?: number;
maxParticipants?: number;
}): Promise<{ url: string; name: string }> {
const response = await fetch('https://api.daily.co/v1/rooms', {
method: 'POST',
headers: {
'Authorization': `Bearer ${DAILY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: options.name ?? `room-${Date.now()}`,
properties: {
exp: Math.floor(Date.now() / 1000) + (options.expiresInMinutes ?? 60) * 60,
max_participants: options.maxParticipants ?? 10,
enable_screenshare: true,
enable_chat: true,
enable_recording: false,
start_video_off: false,
start_audio_off: false,
},
}),
});
const room = await response.json();
return { url: room.url, name: room.name };
}
// API to create a session
app.post('/api/video/room', authenticate, async (req, res) => {
const { consultationId } = req.body;
const room = await createRoom({
name: `consultation-${consultationId}`,
expiresInMinutes: 90,
maxParticipants: 2,
});
await db.consultations.update(consultationId, { roomUrl: room.url });
res.json({ url: room.url });
});
Participant token — mandatory for private rooms. Generated on the backend and passed to the client via API:
async function createMeetingToken(roomName: string, participantName: string, isOwner = false) {
const response = await fetch('https://api.daily.co/v1/meeting-tokens', {
method: 'POST',
headers: {
'Authorization': `Bearer ${DAILY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
properties: {
room_name: roomName,
user_name: participantName,
is_owner: isOwner,
exp: Math.floor(Date.now() / 1000) + 90 * 60,
enable_recording: isOwner,
},
}),
});
const { token } = await response.json();
return token;
}
React component with Daily JS SDK
npm install @daily-co/daily-js
import DailyIframe from '@daily-co/daily-js';
import { useEffect, useRef, useState } from 'react';
interface VideoCallProps {
roomUrl: string;
token: string;
userName: string;
onLeave?: () => void;
}
function VideoCall({ roomUrl, token, userName, onLeave }: VideoCallProps) {
const callRef = useRef<ReturnType<typeof DailyIframe.createFrame> | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [status, setStatus] = useState<'idle' | 'joining' | 'joined' | 'left'>('idle');
useEffect(() => {
if (!containerRef.current) return;
const call = DailyIframe.createFrame(containerRef.current, {
showLeaveButton: true,
showFullscreenButton: true,
iframeStyle: { width: '100%', height: '100%', border: 'none', borderRadius: '12px' },
});
callRef.current = call;
call.on('joining-meeting', () => setStatus('joining'));
call.on('joined-meeting', () => setStatus('joined'));
call.on('left-meeting', () => {
setStatus('left');
onLeave?.();
});
call.on('error', (err) => console.error('Daily error:', err));
call.join({ url: roomUrl, token, userName });
return () => {
call.destroy();
};
}, [roomUrl, token]);
return (
<div className="relative" style={{ height: 600 }}>
{status === 'joining' && (
<div className="absolute inset-0 flex items-center justify-center bg-gray-900 rounded-xl z-10">
<p className="text-white">Connecting...</p>
</div>
)}
{status === 'left' && (
<div className="absolute inset-0 flex items-center justify-center bg-gray-900 rounded-xl z-10">
<p className="text-white text-lg">Call ended</p>
</div>
)}
<div ref={containerRef} className="w-full h-full" />
</div>
);
}
How to set up webhooks?
Daily.co sends events to your server — room status, participant connections, call duration:
app.post('/api/webhooks/daily', async (req, res) => {
const { event_type, payload } = req.body;
switch (event_type) {
case 'meeting.started':
await db.videoSessions.markStarted(payload.room.name);
break;
case 'meeting.ended':
await db.videoSessions.markEnded(payload.room.name, {
duration: payload.meeting_duration,
});
break;
case 'participant.joined':
await db.videoSessions.addParticipant(payload.room.name, payload.participant.user_name);
break;
}
res.status(200).end();
});
Reference: WebRTC API
What stages does the work include?
- Analysis — study load, privacy requirements, need for recording.
- Design — architecture: backend (room creation, tokens), frontend (component), webhooks.
- Implementation — write code, configure API keys, integrate with CRM/ERP.
- Testing — test parallel calls, video quality on 3G, recovery after network loss.
- Deployment — deploy on your server or cloud, train your team.
What's included in the work
- Full integration code (backend + frontend) with comments
- API and webhook setup documentation
- Access to test environment
- Developer training (1 hour)
- One month of technical support after launch
Timelines
| Integration level | Timeline |
|---|---|
| Basic (room + iframe) | 1–2 days |
| Intermediate (+ tokens, webhooks) | 3–4 days |
| Full (+ recording, custom UI) | 5–7 days |
The cost is calculated individually. We'll assess your project within one working day. Our team has delivered over 30 WebRTC projects. With 5+ years of experience, we provide reliable integration. For browser video call, we use the Daily.co SDK. For video consultations on site, we ensure privacy and security. Get an estimate for your project — reach out for a consultation. Contact us to discuss integration. Get a consultation — we'll evaluate your project in one day.







