Skip to Content
Dial v1 live 🎉
SdkWallet-to-Wallet Calls

Wallet-to-Wallet Calls

Make audio and video calls directly between wallet addresses—no phone numbers required.

Starting a Call

Audio Call

const call = await userDialer.calls.start({ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', type: 'audio' }); console.log('Call ID:', call.id);

Video Call

const call = await userDialer.calls.start({ to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', type: 'video', video: { resolution: '720p', frameRate: 30 } });

Call Object

interface Call { id: string; from: string; // Caller wallet address to: string; // Recipient wallet address type: 'audio' | 'video'; status: 'ringing' | 'active' | 'ended' | 'missed' | 'declined'; startedAt?: Date; endedAt?: Date; duration?: number; // in seconds }

Answering a Call

// Listen for incoming calls userDialer.on('call:incoming', async (call) => { // Get caller's profile info const callerProfile = await userDialer.profile.getProfile({ walletAddress: call.from }); console.log(`Incoming call from ${callerProfile.displayName || call.from}`); // Answer the call await userDialer.calls.answer(call.id); });

Declining a Call

await userDialer.calls.decline(call.id, { reason: 'busy' // 'busy', 'declined', or custom message });

Ending a Call

await userDialer.calls.end(call.id);

Media Controls

Mute/Unmute

// Mute your microphone await userDialer.calls.mute(call.id); // Unmute await userDialer.calls.unmute(call.id); // Toggle await userDialer.calls.toggleMute(call.id);

Video Controls

// Disable video await userDialer.calls.disableVideo(call.id); // Enable video await userDialer.calls.enableVideo(call.id); // Toggle video await userDialer.calls.toggleVideo(call.id);

Speaker Controls

// Enable speaker await userDialer.calls.setSpeaker(call.id, true); // Use earpiece await userDialer.calls.setSpeaker(call.id, false);

Call Events

Subscribe to call events for real-time updates:

// Incoming call userDialer.on('call:incoming', (call) => { console.log('Incoming call from:', call.from); }); // Call answered userDialer.on('call:answered', (call) => { console.log('Call answered:', call.id); }); // Call ended userDialer.on('call:ended', (call) => { console.log('Call ended. Duration:', call.duration); }); // Call declined userDialer.on('call:declined', (call) => { console.log('Call declined by:', call.to); }); // Call missed userDialer.on('call:missed', (call) => { console.log('Missed call from:', call.from); });

Media Streams

Accessing Local Stream

const localStream = userDialer.calls.getLocalStream(call.id); // Attach to video element const videoElement = document.getElementById('local-video'); videoElement.srcObject = localStream;

Accessing Remote Stream

const remoteStream = userDialer.calls.getRemoteStream(call.id); // Attach to video element const videoElement = document.getElementById('remote-video'); videoElement.srcObject = remoteStream;

Complete Example: React Component

import { useState, useEffect } from 'react'; import { DialClient } from '@dial/sdk'; export function CallInterface({ userDialer, recipient }) { const [call, setCall] = useState(null); const [isMuted, setIsMuted] = useState(false); const [isVideoEnabled, setIsVideoEnabled] = useState(true); useEffect(() => { // Listen for incoming calls userDialer.on('call:incoming', (incomingCall) => { setCall(incomingCall); }); userDialer.on('call:ended', () => { setCall(null); }); return () => { userDialer.off('call:incoming'); userDialer.off('call:ended'); }; }, [userDialer]); const startCall = async (type: 'audio' | 'video') => { const newCall = await userDialer.calls.start({ to: recipient, type }); setCall(newCall); }; const answerCall = async () => { if (call) { await userDialer.calls.answer(call.id); } }; const endCall = async () => { if (call) { await userDialer.calls.end(call.id); setCall(null); } }; const toggleMute = async () => { if (call) { await userDialer.calls.toggleMute(call.id); setIsMuted(!isMuted); } }; const toggleVideo = async () => { if (call) { await userDialer.calls.toggleVideo(call.id); setIsVideoEnabled(!isVideoEnabled); } }; return ( <div> {!call ? ( <div> <button onClick={() => startCall('audio')}> Audio Call </button> <button onClick={() => startCall('video')}> Video Call </button> </div> ) : ( <div> {call.status === 'ringing' && ( <button onClick={answerCall}>Answer</button> )} <video ref={(el) => { if (el) { el.srcObject = userDialer.calls.getRemoteStream(call.id); } }} autoPlay /> <button onClick={toggleMute}> {isMuted ? 'Unmute' : 'Mute'} </button> {call.type === 'video' && ( <button onClick={toggleVideo}> {isVideoEnabled ? 'Disable Video' : 'Enable Video'} </button> )} <button onClick={endCall}>End Call</button> </div> )} </div> ); }

Call Quality

Network Quality Monitoring

userDialer.on('call:quality', (quality) => { console.log('Network quality:', quality.score); // 0-5 console.log('Latency:', quality.latency); // ms console.log('Packet loss:', quality.packetLoss); // % });

Adaptive Bitrate

The SDK automatically adjusts video quality based on network conditions:

const call = await userDialer.calls.start({ to: '0x...', type: 'video', adaptiveBitrate: true, // default: true minBitrate: 200, // kbps maxBitrate: 2500 // kbps });

Advanced Options

Custom Ring Tones

userDialer.calls.setRingtone('/sounds/custom-ringtone.mp3');

Call Recording

// Start recording const recording = await userDialer.calls.startRecording(call.id); // Stop recording const recordedFile = await userDialer.calls.stopRecording(call.id); console.log('Recording URL:', recordedFile.url);

Caller ID & Profiles

All calls automatically include the caller’s Dial Profile information:

userDialer.on('call:incoming', async (call) => { // Call includes embedded profile info const profile = call.callerProfile; console.log('Display Name:', profile.displayName); console.log('Avatar:', profile.avatar); console.log('Status:', profile.status); // Or fetch full profile separately const fullProfile = await userDialer.profile.getProfile({ walletAddress: call.from }); });

Users are identified by their wallet addresses, with display names and avatars provided by their Dial Profile.

Next Steps

Last updated on