Skip to main content

Twilio Integration

This guide walks you through integrating Kallglot with Twilio for real-time voice translation on phone calls.

Prerequisites

  • A Twilio account with a phone number
  • A Kallglot API key
  • A web server to receive webhooks

Architecture

Setup Steps

1. Configure Your Twilio Number

In the Twilio Console:
  1. Go to Phone Numbers > Manage > Active Numbers
  2. Select your number
  3. Under Voice & Fax, set:
    • A Call Comes In: Webhook
    • URL: https://your-app.com/webhooks/twilio/voice
    • Method: POST

2. Create the Webhook Handler

Kallglot does not currently provide official SDKs. The helper functions in these examples, such as createKallglotSession(...), represent ordinary HTTPS calls to the API.
import express from 'express';
import twilio from 'twilio';

const app = express();
const VoiceResponse = twilio.twiml.VoiceResponse;

app.post('/webhooks/twilio/voice', express.urlencoded({ extended: false }), async (req, res) => {
  const { CallSid, From, To, Direction } = req.body;

  try {
    // Create a Kallglot session
    const session = await createKallglotSession({
      mode: 'bidirectional_translation',
      source_language: 'de',  // Customer's language
      target_language: 'en',  // Agent's language
      provider: {
        type: 'twilio',
        call_sid: CallSid
      },
      metadata: {
        from: From,
        to: To,
        direction: Direction
      }
    });

    // Generate TwiML to connect the call to Kallglot
    const response = new VoiceResponse();

    // Optional: Play a greeting
    response.say({
      language: 'de-DE'
    }, 'Willkommen. Ihr Anruf wird übersetzt.');

    // Connect to Kallglot media stream
    const connect = response.connect();
    connect.stream({
      url: session.stream.url,
      track: 'both_tracks'  // Send both inbound and outbound audio
    }).parameter({
      name: 'token',
      value: session.stream.token
    }).parameter({
      name: 'session_id',
      value: session.id
    });

    res.type('text/xml').send(response.toString());

  } catch (error) {
    console.error('Failed to create session:', error);

    // Fallback: connect to agent without translation
    const response = new VoiceResponse();
    response.say('Translation is temporarily unavailable. Connecting you now.');
    response.dial('+14155550123'); // Your agent's number

    res.type('text/xml').send(response.toString());
  }
});

app.listen(3000);

3. Handle Session Events

Set up a webhook to receive session events:
app.post('/webhooks/kallglot', express.json(), async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'session.ended':
      // Session ended - save transcript, trigger analysis
      const { session_id, duration } = event.data;
      console.log(`Session ${session_id} ended after ${duration}s`);

      // Request analysis
      await requestKallglotAnalysis(session_id, {
        analyses: ['sentiment', 'summary', 'action_items']
      });
      break;

    case 'analysis.complete':
      // Analysis ready - store results
      const { results } = event.data;
      await db.calls.update({
        session_id: event.data.session_id,
        sentiment: results.sentiment.label,
        summary: results.summary.brief
      });
      break;
  }

  res.status(200).send('OK');
});

Advanced Configurations

Outbound Calls

For outbound calls, create the session first, then initiate the call:
async function makeOutboundCall(customerPhone, agentPhone) {
  // 1. Create Kallglot session
  const session = await createKallglotSession({
    mode: 'bidirectional_translation',
    source_language: 'de',
    target_language: 'en'
  });

  // 2. Initiate outbound call via Twilio
  const call = await twilioClient.calls.create({
    url: `https://your-app.com/webhooks/twilio/outbound?session_id=${session.id}`,
    to: customerPhone,
    from: agentPhone
  });

  return { session, call };
}

// Webhook for outbound calls
app.post('/webhooks/twilio/outbound', async (req, res) => {
  const sessionId = req.query.session_id;
  const session = await retrieveKallglotSession(sessionId);

  const response = new VoiceResponse();
  const connect = response.connect();
  connect.stream({
    url: session.stream.url,
    track: 'both_tracks'
  }).parameter({
    name: 'token',
    value: session.stream.token
  });

  res.type('text/xml').send(response.toString());
});

Conference Calls

For multi-party calls, use Twilio Conferences:
app.post('/webhooks/twilio/conference', async (req, res) => {
  const { CallSid, ConferenceSid } = req.body;

  // Create session for this participant
  const session = await createKallglotSession({
    mode: 'bidirectional_translation',
    source_language: 'auto',  // Auto-detect each participant's language
    target_language: 'en'
  });

  const response = new VoiceResponse();
  const dial = response.dial();

  // Join conference with Kallglot stream
  dial.conference({
    statusCallback: '/webhooks/twilio/conference-status',
    record: 'record-from-start'
  }, 'TranslatedConference');

  // Start media stream
  response.connect().stream({
    url: session.stream.url
  }).parameter({
    name: 'token',
    value: session.stream.token
  });

  res.type('text/xml').send(response.toString());
});

Dynamic Language Detection

Let Kallglot auto-detect the customer’s language:
const session = await createKallglotSession({
  mode: 'bidirectional_translation',
  source_language: 'auto',  // Auto-detect
  target_language: 'en'
});

// Listen for language detection event
session.on('language_detected', (data) => {
  console.log(`Detected language: ${data.language} (${data.confidence})`);
});

Error Handling

Handle Session Creation Failures

app.post('/webhooks/twilio/voice', async (req, res) => {
  try {
    const session = await createKallglotSession({
      // ...
    });
    // Return TwiML with stream
  } catch (error) {
    console.error('Kallglot error:', error);

    const response = new VoiceResponse();

    if (error.code === 'rate_limit_exceeded') {
      response.say('We are experiencing high call volume. Please try again later.');
      response.hangup();
    } else {
      // Fallback to non-translated call
      response.say('Translation service is temporarily unavailable.');
      response.dial('+14155550123');
    }

    res.type('text/xml').send(response.toString());
  }
});

Handle Stream Disconnections

Twilio sends a status callback when the stream disconnects:
app.post('/webhooks/twilio/stream-status', async (req, res) => {
  const { StreamSid, StreamStatus } = req.body;

  if (StreamStatus === 'stopped') {
    // Stream ended - end the Kallglot session
    const sessionId = await getSessionIdForStream(StreamSid);
    await endKallglotSession(sessionId);
  }

  res.status(200).send('OK');
});

Best Practices

Always use track: 'both_tracks' to send both inbound and outbound audio to Kallglot. This ensures proper speaker separation.
The Twilio webhook must respond quickly (within 15 seconds). Create the session and respond immediately:
// Good: Create session in webhook
const session = await createKallglotSession({...});
res.type('text/xml').send(response.toString());

// Bad: Doing too much in webhook
const session = await createKallglotSession({...});
await db.save(session);  // Too slow!
await analytics.track({...});  // Even slower!
res.type('text/xml').send(response.toString());
End Kallglot sessions when calls complete to stop billing:
app.post('/webhooks/twilio/status', async (req, res) => {
  if (req.body.CallStatus === 'completed') {
    const sessionId = await getSessionIdForCall(req.body.CallSid);
    await endKallglotSession(sessionId);
  }
  res.status(200).send('OK');
});

Troubleshooting

No audio in translation

  1. Verify the stream URL is correct
  2. Check that track: 'both_tracks' is set
  3. Verify the WebSocket connection is established

High latency

  1. Ensure your server is geographically close to Twilio’s region
  2. Check network latency between your server and Kallglot
  3. Consider using Twilio’s edge locations

Echo or feedback

  1. Ensure you’re not playing translated audio back to the source
  2. Check that speaker identification is working correctly