> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kallglot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Telnyx Integration

> Connect Telnyx phone calls to Kallglot

# Telnyx Integration

This guide walks you through integrating Kallglot with Telnyx for real-time voice translation.

## Prerequisites

* A Telnyx account with a phone number
* A TeXML application configured
* A Kallglot API key
* A web server to receive webhooks

## Architecture

```mermaid theme={null}
graph LR
    subgraph Telnyx
        P[Phone Number]
        CC[Call Control]
        M[Media Streaming]
    end

    subgraph Your App
        W[Webhook Handler]
    end

    subgraph Kallglot
        S[Session]
        WS[WebSocket]
    end

    P -->|Incoming Call| W
    W -->|Create Session| S
    W -->|TeXML Response| CC
    CC --> M
    M -->|Audio Stream| WS
    WS -->|Translated Audio| M
```

## Setup Steps

### 1. Create a TeXML Application

In the Telnyx Portal:

1. Go to **Call Control** > **TeXML Applications**
2. Click **Create TeXML Application**
3. Configure:
   * **Name**: Kallglot Translation
   * **Voice URL**: `https://your-app.com/webhooks/telnyx/voice`
   * **Voice Method**: POST
4. Note the Application ID

### 2. Assign Phone Number

1. Go to **Numbers** > **My Numbers**
2. Select your number
3. Under **Voice**, select your TeXML application
4. Save

### 3. Create the Webhook Handler

<Note>
  Kallglot does not currently provide official SDKs. The helper functions in these examples, such as `createKallglotSession(...)`, represent standard HTTP requests to the API.
</Note>

```javascript theme={null}
import express from 'express';

const app = express();

app.post('/webhooks/telnyx/voice', express.json(), async (req, res) => {
  const event = req.body.data;
  const { call_control_id, from, to, direction } = event.payload;

  try {
    // Create a Kallglot session (telephony selection uses `routing`, not `provider`)
    const session = await createKallglotSession({
      mode: 'bidirectional_translation',
      source_language: 'de',
      target_language: 'en',
      routing: {
        phone_number: to
      },
      metadata: {
        telnyx_call_control_id: call_control_id,
        from,
        to,
        direction
      }
    });

    // Generate TeXML to connect the call to Kallglot
    const texml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say language="de-DE">Willkommen. Ihr Anruf wird übersetzt.</Say>
  <Connect>
    <Stream url="${session.stream.url}" track="both_tracks">
      <Parameter name="token" value="${session.stream.token}"/>
      <Parameter name="session_id" value="${session.id}"/>
    </Stream>
  </Connect>
</Response>`;

    res.type('text/xml').send(texml);

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

    // Fallback without translation
    const texml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say>Translation is temporarily unavailable. Connecting you now.</Say>
  <Dial>+14155550123</Dial>
</Response>`;

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

app.listen(3000);
```

### 4. Handle Call Events

Set up status callbacks for call lifecycle events:

```javascript theme={null}
app.post('/webhooks/telnyx/status', express.json(), async (req, res) => {
  const event = req.body.data;
  const eventType = event.event_type;
  const { call_control_id } = event.payload;

  switch (eventType) {
    case 'call.hangup':
      // Call ended - end the Kallglot session
      const sessionId = await getSessionIdForCall(call_control_id);
      if (sessionId) {
        await endKallglotSession(sessionId, {
          reason: event.payload.hangup_cause
        });
      }
      break;

    case 'call.answered':
      console.log('Call answered');
      break;

    case 'streaming.started':
      console.log('Media streaming started');
      break;

    case 'streaming.stopped':
      console.log('Media streaming stopped');
      break;
  }

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

## Advanced Configurations

### Outbound Calls with Call Control API

For more control, use Telnyx's Call Control API:

```javascript theme={null}
import Telnyx from 'telnyx';

const telnyx = new Telnyx(process.env.TELNYX_API_KEY);

async function makeOutboundCall(customerPhone, fromNumber) {
  // 1. Create Kallglot session
  const session = await createKallglotSession({
    mode: 'bidirectional_translation',
    source_language: 'de',
    target_language: 'en'
  });

  // 2. Store session for webhook lookup
  await db.pendingCalls.insert({
    session_id: session.id,
    to: customerPhone
  });

  // 3. Initiate call via Telnyx
  const call = await telnyx.calls.create({
    connection_id: process.env.TELNYX_CONNECTION_ID,
    to: customerPhone,
    from: fromNumber,
    webhook_url: 'https://your-app.com/webhooks/telnyx/outbound'
  });

  return { session, call };
}

// Webhook for outbound call events
app.post('/webhooks/telnyx/outbound', express.json(), async (req, res) => {
  const event = req.body.data;

  if (event.event_type === 'call.initiated') {
    // Look up session for this call
    const pending = await db.pendingCalls.findOne({
      to: event.payload.to
    });

    if (pending) {
      const session = await retrieveKallglotSession(pending.session_id);

      // Answer and start streaming
      await telnyx.calls.answer(event.payload.call_control_id);

      await telnyx.calls.startStream(event.payload.call_control_id, {
        stream_url: session.stream.url,
        stream_track: 'both_tracks'
      });
    }
  }

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

### Using SIP Trunking

For SIP-based integration with your PBX:

```javascript theme={null}
const session = await createKallglotSession({
  mode: 'bidirectional_translation',
  source_language: 'de',
  target_language: 'en',
  routing: {
    type: 'sip',
    sip: {
      to_uri: 'sip:agent@your-pbx.com'
    }
  }
});

// Configure your PBX to route calls per your SIP integration guide (see SIP Integration).
```

### Recording Configuration

Configure call recording in TeXML:

```xml theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Record
    recordingStatusCallback="https://your-app.com/webhooks/telnyx/recording"
    recordingStatusCallbackEvent="completed"
    transcribe="false"
  />
  <Connect>
    <Stream url="${session.stream.url}" track="both_tracks">
      <Parameter name="token" value="${session.stream.token}"/>
    </Stream>
  </Connect>
</Response>
```

## Telnyx vs Twilio Comparison

| Feature         | Telnyx           | Twilio          |
| --------------- | ---------------- | --------------- |
| Pricing         | Generally lower  | Higher          |
| Global coverage | 100+ countries   | 180+ countries  |
| SIP Trunking    | Native support   | Via Elastic SIP |
| Media streams   | Yes              | Yes             |
| API style       | REST + WebSocket | REST + TwiML    |
| Number porting  | Yes              | Yes             |

## Error Handling

### Handle API Errors

```javascript theme={null}
app.post('/webhooks/telnyx/voice', async (req, res) => {
  try {
    const session = await createKallglotSession({
      // ...
    });

    // Success - return TeXML
    res.type('text/xml').send(buildTeXML(session));

  } catch (error) {
    console.error('Error:', error);

    // Check error type
    if (error.code === 'rate_limit_exceeded') {
      // Return busy signal
      res.type('text/xml').send(`
        <Response>
          <Reject reason="busy"/>
        </Response>
      `);
    } else {
      // Fallback to non-translated call
      res.type('text/xml').send(`
        <Response>
          <Say>Translation unavailable. Please hold.</Say>
          <Dial>+14155550123</Dial>
        </Response>
      `);
    }
  }
});
```

### Handle Webhook Signature Verification

Telnyx signs webhooks with a signature you should verify:

```javascript theme={null}
import crypto from 'crypto';

function verifyTelnyxSignature(req) {
  const signature = req.headers['telnyx-signature-ed25519'];
  const timestamp = req.headers['telnyx-timestamp'];
  const publicKey = process.env.TELNYX_PUBLIC_KEY;

  const signedPayload = `${timestamp}|${JSON.stringify(req.body)}`;

  // Verify using Ed25519
  return crypto.verify(
    null,
    Buffer.from(signedPayload),
    publicKey,
    Buffer.from(signature, 'base64')
  );
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use Call Control for complex flows">
    For multi-step call flows (IVR, transfers), use the Call Control API instead of TeXML for more flexibility.
  </Accordion>

  <Accordion title="Handle network issues gracefully">
    Telnyx webhooks may timeout. Respond quickly and process asynchronously:

    ```javascript theme={null}
    app.post('/webhooks/telnyx/voice', async (req, res) => {
      // Respond immediately
      res.type('text/xml').send(buildQuickResponse());

      // Process in background
      setImmediate(async () => {
        await processCall(req.body);
      });
    });
    ```
  </Accordion>

  <Accordion title="Use regional connections">
    Configure Telnyx connections in regions close to your customers to minimize latency.
  </Accordion>
</AccordionGroup>

## Troubleshooting

### Calls not connecting

1. Verify the TeXML application is assigned to your number
2. Check webhook URL is accessible (use ngrok for testing)
3. Verify TeXML syntax is valid

### No audio translation

1. Check `track="both_tracks"` is set in Stream element
2. Verify Kallglot session was created successfully
3. Check WebSocket connection in stream logs

### High latency

1. Check region configuration
2. Verify network path between Telnyx and your server
3. Consider using Telnyx's edge locations closer to your users
