> ## 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.

# Quick Start

> Create your first real-time voice session in 5 minutes

# Quick Start Guide

This guide walks you through creating a real-time voice translation session using the Kallglot API.

## Choose Your Telephony Path

Before creating a session, decide how the call will reach Kallglot:

1. **Kallglot-managed number**: use an active number provisioned by Kallglot
2. **External provider connection**: use your own Twilio or Telnyx account via `routing.connection_id`
3. **SIP / PBX**: create the session first, then route your PBX call to `sip:<session_id>@sip.kallglot.com`

See [Choose your telephony integration](/guides/choose-telephony-integration) for the routing guide.

## Prerequisites

<Steps>
  <Step title="Get an API Key">
    Create your developer account at [kallglot.com](https://www.kallglot.com/users/sign_up), then generate an API key in the Developer Portal.
  </Step>

  <Step title="Activate Billing">
    Make sure the organization has an active subscription and available API credits before sending production API requests.
  </Step>
</Steps>

## Base URLs

| Environment | Base URL                      |
| ----------- | ----------------------------- |
| REST API    | `https://api.kallglot.com/v1` |
| WebSocket   | `wss://api.kallglot.com/v1`   |

## Create a Session

A session establishes a real-time voice connection with Kallglot. The example below uses a Kallglot-managed number because it is the fastest setup path.

<Warning>
  **Request shape: use `routing`, not `provider`.** For `POST /v1/sessions`, telephony selection belongs under **`routing`** (`phone_number`, `connection_id`, or SIP fields per [Create Session](/api-reference/sessions/create)). The session **response** may include a string field **`provider`** (for example `"twilio"`) describing which carrier was resolved—that is **not** something you send as `provider: { "type": "twilio", ... }` in the request body. Older samples that used a nested `provider` object for outbound creation are **not** the public contract; integrations should always follow `routing`.
</Warning>

<Tip>
  **Language auto-detection**: `source_language` and `target_language` are optional. When omitted, Kallglot infers each speaker’s language from the audio. Set them explicitly when you already know the pair for more predictable startup behavior.
</Tip>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.kallglot.com/v1/sessions \
    -H "Authorization: Bearer sk_live_your_api_key" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(uuidgen)" \
    -d '{
      "mode": "bidirectional_translation",
      "source_language": "de",
      "target_language": "en",
      "routing": {
        "phone_number": "+19788006140"
      },
      "metadata": {
        "external_call_id": "call_abc123"
      }
    }'
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'
  require 'securerandom'

  def create_session(source_lang:, target_lang:, metadata: {})
    uri = URI('https://api.kallglot.com/v1/sessions')

    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{ENV['KALLGLOT_API_KEY']}"
    request['Content-Type'] = 'application/json'
    request['Idempotency-Key'] = SecureRandom.uuid

    request.body = {
      mode: 'bidirectional_translation',
      source_language: source_lang,
      target_language: target_lang,
      routing: {
        phone_number: '+19788006140'
      },
      metadata: metadata
    }.to_json

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body)
  end

  # Usage
  session = create_session(
    source_lang: 'de',
    target_lang: 'en',
    metadata: { call_id: 'call_abc123' }
  )

  puts "Session ID: #{session['id']}"
  puts "WebSocket URL: #{session['stream']['url']}"
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "net/http"
      "os"

      "github.com/google/uuid"
  )

  type SessionRequest struct {
      Mode           string            `json:"mode"`
      SourceLanguage string            `json:"source_language"`
      TargetLanguage string            `json:"target_language"`
      Routing        map[string]string `json:"routing"`
      Metadata       map[string]string `json:"metadata,omitempty"`
  }

  type SessionResponse struct {
      ID     string `json:"id"`
      Object string `json:"object"`
      Status string `json:"status"`
      Stream struct {
          URL       string `json:"url"`
          Token     string `json:"token"`
          ExpiresAt string `json:"expires_at"`
      } `json:"stream"`
  }

  func createSession(sourceLang, targetLang string) (*SessionResponse, error) {
      reqBody := SessionRequest{
          Mode:           "bidirectional_translation",
          SourceLanguage: sourceLang,
          TargetLanguage: targetLang,
          Routing:        map[string]string{"phone_number": "+19788006140"},
          Metadata:       map[string]string{"call_id": "call_abc123"},
      }

      jsonBody, _ := json.Marshal(reqBody)

      req, _ := http.NewRequest("POST", "https://api.kallglot.com/v1/sessions", bytes.NewBuffer(jsonBody))
      req.Header.Set("Authorization", "Bearer "+os.Getenv("KALLGLOT_API_KEY"))
      req.Header.Set("Content-Type", "application/json")
      req.Header.Set("Idempotency-Key", uuid.New().String())

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          return nil, err
      }
      defer resp.Body.Close()

      var session SessionResponse
      json.NewDecoder(resp.Body).Decode(&session)

      return &session, nil
  }

  func main() {
      session, err := createSession("de", "en")
      if err != nil {
          panic(err)
      }

      fmt.Printf("Session ID: %s\n", session.ID)
      fmt.Printf("WebSocket URL: %s\n", session.Stream.URL)
  }
  ```

  ```javascript JavaScript theme={null}
  async function createSession(sourceLang, targetLang, metadata = {}) {
    const response = await fetch('https://api.kallglot.com/v1/sessions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.KALLGLOT_API_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': crypto.randomUUID()
      },
      body: JSON.stringify({
        mode: 'bidirectional_translation',
        source_language: sourceLang,
        target_language: targetLang,
        routing: {
          phone_number: '+19788006140'
        },
        metadata
      })
    });

    return response.json();
  }

  // Usage
  const session = await createSession('de', 'en', { call_id: 'call_abc123' });
  console.log('Session ID:', session.id);
  console.log('WebSocket URL:', session.stream.url);
  ```

  ```python Python theme={null}
  import os
  import uuid
  import requests

  def create_session(source_lang: str, target_lang: str, metadata: dict = None):
      response = requests.post(
          'https://api.kallglot.com/v1/sessions',
          headers={
              'Authorization': f'Bearer {os.environ["KALLGLOT_API_KEY"]}',
              'Content-Type': 'application/json',
              'Idempotency-Key': str(uuid.uuid4())
          },
          json={
              'mode': 'bidirectional_translation',
              'source_language': source_lang,
              'target_language': target_lang,
              'routing': {
                  'phone_number': '+19788006140'
              },
              'metadata': metadata or {}
          }
      )
      return response.json()

  # Usage
  session = create_session('de', 'en', {'call_id': 'call_abc123'})
  print(f"Session ID: {session['id']}")
  print(f"WebSocket URL: {session['stream']['url']}")
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "sess_01HXYZ",
  "object": "session",
  "status": "created",
  "mode": "bidirectional_translation",
  "source_language": "de",
  "target_language": "en",
  "stream": {
    "url": "wss://api.kallglot.com/v1/sessions/sess_01HXYZ/connect",
    "token": "kst_live_xxxxxxxx",
    "expires_at": "2026-03-10T12:00:00Z"
  },
  "call": null,
  "created_at": "2026-03-10T11:55:00Z"
}
```

<Note>
  Successful `POST /v1/sessions` requests currently consume 1 API credit. Check `X-API-Credits-Remaining` and `X-API-Credits-Used` in the response headers to track usage. If the organization subscription is inactive, the API returns `403 organization_inactive`. If credits are exhausted, it returns `402 insufficient_api_credits`.
</Note>

## Connect to the WebSocket

The session response includes a WebSocket URL for real-time streaming. The token expires in 5 minutes, so connect promptly. Once connected, token expiry does NOT terminate the stream.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function connectToSession(sessionId, streamToken) {
    const ws = new WebSocket(
      `wss://api.kallglot.com/v1/sessions/${sessionId}/connect?token=${streamToken}`
    );

    ws.onopen = () => {
      console.log('Connected to Kallglot stream');
    };

    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);

      switch (data.type) {
        case 'session.ready':
          console.log('Session ready');
          break;

        case 'transcript.partial':
          console.log(`[Partial] ${data.speaker}: ${data.text}`);
          break;

        case 'transcript.final':
          console.log(`[${data.speaker}] ${data.text}`);
          if (data.translation) {
            console.log(`  → ${data.translation.text}`);
          }
          break;

        case 'audio.output':
          // Play translated audio
          playAudio(data.audio.payload);
          break;

        case 'session.ended':
          console.log('Session ended:', data.reason);
          ws.close();
          break;

        case 'error':
          console.error(`Error [${data.code}]: ${data.message}`);
          break;
      }
    };

    return ws;
  }

  // Send audio to the stream
  function sendAudio(ws, audioBase64, speaker) {
    ws.send(JSON.stringify({
      type: 'audio.input',
      sequence: Date.now(),
      timestamp_ms: Date.now(),
      speaker: speaker,
      audio: {
        encoding: 'mulaw',
        sample_rate_hz: 8000,
        payload: audioBase64
      }
    }));
  }
  ```

  ```ruby Ruby theme={null}
  require 'websocket-client-simple'
  require 'json'

  def connect_to_session(session_id, stream_token)
    url = "wss://api.kallglot.com/v1/sessions/#{session_id}/connect?token=#{stream_token}"

    ws = WebSocket::Client::Simple.connect(url)

    ws.on :open do
      puts 'Connected to Kallglot stream'
    end

    ws.on :message do |msg|
      data = JSON.parse(msg.data)

      case data['type']
      when 'session.ready'
        puts 'Session ready'
      when 'transcript.partial'
        puts "[Partial] #{data['speaker']}: #{data['text']}"
      when 'transcript.final'
        puts "[#{data['speaker']}] #{data['text']}"
        if data['translation']
          puts "  → #{data['translation']['text']}"
        end
      when 'audio.output'
        # Handle translated audio
        handle_audio(data['audio']['payload'])
      when 'session.ended'
        puts "Session ended: #{data['reason']}"
        ws.close
      when 'error'
        puts "Error [#{data['code']}]: #{data['message']}"
      end
    end

    ws.on :error do |e|
      puts "Error: #{e.message}"
    end

    ws
  end

  def send_audio(ws, audio_base64, speaker)
    ws.send({
      type: 'audio.input',
      sequence: Time.now.to_i,
      timestamp_ms: (Time.now.to_f * 1000).to_i,
      speaker: speaker,
      audio: {
        encoding: 'mulaw',
        sample_rate_hz: 8000,
        payload: audio_base64
      }
    }.to_json)
  end
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "fmt"
      "log"
      "time"

      "github.com/gorilla/websocket"
  )

  type AudioInput struct {
      Type        string `json:"type"`
      Sequence    int64  `json:"sequence"`
      TimestampMs int64  `json:"timestamp_ms"`
      Speaker     string `json:"speaker"`
      Audio       struct {
          Encoding     string `json:"encoding"`
          SampleRateHz int    `json:"sample_rate_hz"`
          Payload      string `json:"payload"`
      } `json:"audio"`
  }

  func connectToSession(sessionID, streamToken string) (*websocket.Conn, error) {
      url := fmt.Sprintf("wss://api.kallglot.com/v1/sessions/%s/connect?token=%s", sessionID, streamToken)

      conn, _, err := websocket.DefaultDialer.Dial(url, nil)
      if err != nil {
          return nil, err
      }

      go func() {
          for {
              _, message, err := conn.ReadMessage()
              if err != nil {
                  log.Println("Read error:", err)
                  return
              }

              var data map[string]interface{}
              json.Unmarshal(message, &data)

              switch data["type"] {
              case "session.ready":
                  fmt.Println("Session ready")
              case "transcript.partial":
                  fmt.Printf("[Partial] %s: %s\n", data["speaker"], data["text"])
              case "transcript.final":
                  fmt.Printf("[%s] %s\n", data["speaker"], data["text"])
                  if trans, ok := data["translation"].(map[string]interface{}); ok {
                      fmt.Printf("  → %s\n", trans["text"])
                  }
              case "session.ended":
                  fmt.Printf("Session ended: %s\n", data["reason"])
                  return
              case "error":
                  fmt.Printf("Error [%s]: %s\n", data["code"], data["message"])
              }
          }
      }()

      return conn, nil
  }

  func sendAudio(conn *websocket.Conn, audioBase64, speaker string) error {
      input := AudioInput{
          Type:        "audio.input",
          Sequence:    time.Now().Unix(),
          TimestampMs: time.Now().UnixMilli(),
          Speaker:     speaker,
      }
      input.Audio.Encoding = "mulaw"
      input.Audio.SampleRateHz = 8000
      input.Audio.Payload = audioBase64

      return conn.WriteJSON(input)
  }
  ```

  ```python Python theme={null}
  import asyncio
  import websockets
  import json
  import time

  async def connect_to_session(session_id: str, stream_token: str):
      url = f"wss://api.kallglot.com/v1/sessions/{session_id}/connect?token={stream_token}"

      async with websockets.connect(url) as ws:
          async for message in ws:
              data = json.loads(message)

              if data['type'] == 'session.ready':
                  print('Session ready')

              elif data['type'] == 'transcript.partial':
                  print(f"[Partial] {data['speaker']}: {data['text']}")

              elif data['type'] == 'transcript.final':
                  print(f"[{data['speaker']}] {data['text']}")
                  if 'translation' in data:
                      print(f"  → {data['translation']['text']}")

              elif data['type'] == 'audio.output':
                  # Handle translated audio
                  handle_audio(data['audio']['payload'])

              elif data['type'] == 'session.ended':
                  print(f"Session ended: {data['reason']}")
                  break

              elif data['type'] == 'error':
                  print(f"Error [{data['code']}]: {data['message']}")

  async def send_audio(ws, audio_base64: str, speaker: str):
      await ws.send(json.dumps({
          'type': 'audio.input',
          'sequence': int(time.time()),
          'timestamp_ms': int(time.time() * 1000),
          'speaker': speaker,
          'audio': {
              'encoding': 'mulaw',
              'sample_rate_hz': 8000,
              'payload': audio_base64
          }
      }))
  ```
</CodeGroup>

## End the Session

When the conversation is complete, end the session to stop billing:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.kallglot.com/v1/sessions/sess_01HXYZ/end \
    -H "Authorization: Bearer sk_live_your_api_key" \
    -H "Idempotency-Key: $(uuidgen)"
  ```

  ```ruby Ruby theme={null}
  def end_session(session_id)
    uri = URI("https://api.kallglot.com/v1/sessions/#{session_id}/end")

    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{ENV['KALLGLOT_API_KEY']}"
    request['Idempotency-Key'] = SecureRandom.uuid

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body)
  end

  session = end_session('sess_01HXYZ')
  puts "Duration: #{session['duration_seconds']} seconds"
  ```

  ```go Go theme={null}
  func endSession(sessionID string) (*SessionResponse, error) {
      req, _ := http.NewRequest("POST",
          fmt.Sprintf("https://api.kallglot.com/v1/sessions/%s/end", sessionID),
          nil)

      req.Header.Set("Authorization", "Bearer "+os.Getenv("KALLGLOT_API_KEY"))
      req.Header.Set("Idempotency-Key", uuid.New().String())

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          return nil, err
      }
      defer resp.Body.Close()

      var session SessionResponse
      json.NewDecoder(resp.Body).Decode(&session)

      return &session, nil
  }
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(`https://api.kallglot.com/v1/sessions/${sessionId}/end`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.KALLGLOT_API_KEY}`,
      'Idempotency-Key': crypto.randomUUID()
    }
  });

  const session = await response.json();
  console.log(`Duration: ${session.duration_seconds} seconds`);
  ```

  ```python Python theme={null}
  response = requests.post(
      f'https://api.kallglot.com/v1/sessions/{session_id}/end',
      headers={
          'Authorization': f'Bearer {os.environ["KALLGLOT_API_KEY"]}',
          'Idempotency-Key': str(uuid.uuid4())
      }
  )

  session = response.json()
  print(f"Duration: {session['duration_seconds']} seconds")
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "sess_01HXYZ",
  "object": "session",
  "status": "ended",
  "duration_seconds": 312,
  "ended_at": "2026-03-10T12:05:44Z"
}
```

## Retrieve the Transcript

After the session ends, retrieve the complete transcript:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.kallglot.com/v1/sessions/sess_01HXYZ/transcript \
    -H "Authorization: Bearer sk_live_your_api_key"
  ```

  ```ruby Ruby theme={null}
  def get_transcript(session_id)
    uri = URI("https://api.kallglot.com/v1/sessions/#{session_id}/transcript")

    request = Net::HTTP::Get.new(uri)
    request['Authorization'] = "Bearer #{ENV['KALLGLOT_API_KEY']}"

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    JSON.parse(response.body)
  end

  transcript = get_transcript('sess_01HXYZ')
  transcript['turns'].each do |turn|
    puts "[#{turn['speaker']}] #{turn['text']}"
    puts "  → #{turn['translation']['text']}" if turn['translation']
  end
  ```

  ```go Go theme={null}
  func getTranscript(sessionID string) (map[string]interface{}, error) {
      req, _ := http.NewRequest("GET",
          fmt.Sprintf("https://api.kallglot.com/v1/sessions/%s/transcript", sessionID),
          nil)

      req.Header.Set("Authorization", "Bearer "+os.Getenv("KALLGLOT_API_KEY"))

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          return nil, err
      }
      defer resp.Body.Close()

      var transcript map[string]interface{}
      json.NewDecoder(resp.Body).Decode(&transcript)

      return transcript, nil
  }
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(`https://api.kallglot.com/v1/sessions/${sessionId}/transcript`, {
    headers: {
      'Authorization': `Bearer ${process.env.KALLGLOT_API_KEY}`
    }
  });

  const transcript = await response.json();
  transcript.turns.forEach(turn => {
    console.log(`[${turn.speaker}] ${turn.text}`);
    if (turn.translation) {
      console.log(`  → ${turn.translation.text}`);
    }
  });
  ```

  ```python Python theme={null}
  response = requests.get(
      f'https://api.kallglot.com/v1/sessions/{session_id}/transcript',
      headers={
          'Authorization': f'Bearer {os.environ["KALLGLOT_API_KEY"]}'
      }
  )

  transcript = response.json()
  for turn in transcript['turns']:
      print(f"[{turn['speaker']}] {turn['text']}")
      if 'translation' in turn:
          print(f"  → {turn['translation']['text']}")
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "id": "trn_01HXYZ",
  "object": "transcript",
  "session_id": "sess_01HXYZ",
  "status": "completed",
  "turns": [
    {
      "sequence": 1,
      "speaker": "customer",
      "language": "de",
      "text": "Ich möchte meine Bestellung ändern",
      "translation": {
        "language": "en",
        "text": "I want to change my order"
      },
      "confidence": 0.96,
      "timestamp": "2026-03-10T11:58:02Z"
    }
  ],
  "metadata": {
    "total_turns": 14,
    "duration_seconds": 312
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Session Modes" icon="sliders" href="/concepts/session-modes">
    Learn about different translation modes
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks/overview">
    Set up real-time event notifications
  </Card>

  <Card title="AI Agents" icon="robot" href="/guides/ai-agents">
    Deploy intelligent voice agents
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/errors">
    Handle errors gracefully
  </Card>
</CardGroup>
