> ## Documentation Index
> Fetch the complete documentation index at: https://docs.60db.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Realtime Quickstart

> Run a text turn and a voice turn against 60db Realtime with the OpenAI SDK, Python or a raw WebSocket

This guide opens a Realtime session, sends one text message, and receives a spoken reply. Use the OpenAI SDK you already have, or a raw WebSocket.

**1. Create an API key**

Go to **Settings → Developer → API Keys** in [app.60db.ai](https://app.60db.ai) and create a key. Keep it on your server. The examples read it from `SIXTYDB_API_KEY`.

```bash theme={null}
export SIXTYDB_API_KEY=sk_your_api_key
```

> [!WARNING]
>
> Never ship `sk_` keys to a browser. For web pages, use [browser ephemeral keys](https://docs.60db.ai/realtime/browser-ephemeral-keys.md).

**2. Open a session and send a text turn**

**JavaScript (openai)**

```javascript theme={null}
import OpenAI from 'openai';
import { OpenAIRealtimeWS } from 'openai/realtime/ws';

const client = new OpenAI({
  apiKey: process.env.SIXTYDB_API_KEY,
  baseURL: 'https://api.60db.ai/v1',
});

const rt = await OpenAIRealtimeWS.create(client, { model: '60db-realtime-v1-mini' });

rt.send({
  type: 'session.update',
  session: {
    type: 'realtime',
    output_modalities: ['audio'],
    audio: { output: { voice: 'YOUR_VOICE_ID' } },
    instructions: 'You are a friendly assistant. Keep replies short.',
  },
});

rt.send({
  type: 'conversation.item.create',
  item: {
    type: 'message',
    role: 'user',
    content: [{ type: 'input_text', text: 'Say hello in one sentence.' }],
  },
});
rt.send({ type: 'response.create' });

rt.on('response.output_audio.delta', (event) => {
  // event.delta is base64 PCM16 at 24 kHz mono
});
rt.on('response.done', (event) => {
  console.log('usage:', event.response.usage);
  rt.close();
});
```

**Python (openai)**

```python theme={null}
import asyncio
import os

from openai import AsyncOpenAI

client = AsyncOpenAI(
    api_key=os.environ["SIXTYDB_API_KEY"],
    base_url="https://api.60db.ai/v1",
)

async def main():
    async with client.realtime.connect(model="60db-realtime-v1-mini") as conn:
        await conn.session.update(session={
            "type": "realtime",
            "output_modalities": ["audio"],
            "audio": {"output": {"voice": "YOUR_VOICE_ID"}},
            "instructions": "You are a friendly assistant. Keep replies short.",
        })
        await conn.conversation.item.create(item={
            "type": "message",
            "role": "user",
            "content": [{"type": "input_text", "text": "Say hello in one sentence."}],
        })
        await conn.response.create()

        async for event in conn:
            if event.type == "response.output_audio.delta":
                pass  # event.delta is base64 PCM16 at 24 kHz mono
            if event.type == "response.done":
                print("usage:", event.response.usage)
                break

asyncio.run(main())
```

**Raw WebSocket (Node)**

```javascript theme={null}
// npm install ws
import WebSocket from 'ws';

const ws = new WebSocket(
  'wss://api.60db.ai/v1/realtime?model=60db-realtime-v1-mini',
  { headers: { Authorization: `Bearer ${process.env.SIXTYDB_API_KEY}` } },
);

ws.on('open', () => {
  ws.send(JSON.stringify({
    type: 'session.update',
    session: {
      type: 'realtime',
      output_modalities: ['audio'],
      audio: { output: { voice: 'YOUR_VOICE_ID' } },
    },
  }));
  ws.send(JSON.stringify({
    type: 'conversation.item.create',
    item: {
      type: 'message',
      role: 'user',
      content: [{ type: 'input_text', text: 'Say hello in one sentence.' }],
    },
  }));
  ws.send(JSON.stringify({ type: 'response.create' }));
});

ws.on('message', (raw) => {
  const event = JSON.parse(raw);
  if (event.type === 'response.output_audio_transcript.delta') process.stdout.write(event.delta);
  if (event.type === 'response.done') ws.close();
});
```

Replace `YOUR_VOICE_ID` with a voice ID from [List voices](https://docs.60db.ai/api-reference/voices/get-voices.md).

**3. Send a voice turn**

For voice, stream PCM16 audio into `input_audio_buffer.append`. With the default `server_vad` turn detection, the server commits the utterance after silence and starts the reply on its own.

```json theme={null}
{ "type": "input_audio_buffer.append", "audio": "<base64 PCM16 mono, 24 kHz>" }
```

Listen for these server events:

| Event | What to do |
| - | - |
| `input_audio_buffer.speech_started` | Stop local playback (barge-in) |
| `conversation.item.input_audio_transcription.completed` | Show the user's transcript |
| `response.output_audio.delta` | Play the audio chunk |
| `response.done` | Read `response.usage` |

See [Audio and turn detection](https://docs.60db.ai/realtime/audio-and-turn-detection.md) for formats and manual turn control.

**4. Check the result**

The final `response.done` event carries usage. Each session is billed at \$0.02 per connected minute plus LLM tokens. See [Realtime overview](https://docs.60db.ai/realtime/overview.md#pricing).

## Troubleshooting

| Symptom | Cause | Fix |
| - | - | - |
| `401` or `authentication_error` on connect | Missing or wrong `Authorization` header | Send `Bearer sk_...` from a server |
| Close code `1008` on connect | Authentication failed | Check the key and that it is not deleted |
| Close code `4003` | Concurrent session limit reached | Close idle sessions or upgrade your plan |
| `invalid_value` on `session.audio.output.voice` | The voice is not available to your workspace | Use a stock voice or one of your own voices |

## Next steps

* [Migrate an existing OpenAI app](https://docs.60db.ai/realtime/openai-sdk-migration.md)
* [Function tools](https://docs.60db.ai/realtime/function-tools.md)
* [60db extensions](https://docs.60db.ai/realtime/60db-extensions.md): memory, knowledge and intent


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.