> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.meshapi.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.meshapi.ai/_mcp/server.

# Realtime Audio

> Bidirectional speech-to-speech WebSocket sessions with the Go SDK.

# Realtime Audio

`client.Realtime` opens a bidirectional WebSocket session to `wss://api.meshapi.ai/v1/realtime`. The wire format is identical to OpenAI's Realtime API — every event you send and receive is shaped exactly as upstream documents it.

## Connect and close

```go
session, err := client.Realtime.Connect(ctx, meshapi.RealtimeConnectParams{
    Model: "openai/gpt-realtime-mini",
})
if err != nil {
    log.Fatal(err)
}
defer session.Close()
```

## Configure the session

```go
err = session.Send(ctx, map[string]any{
    "type": "session.update",
    "session": map[string]any{
        "type":         "realtime",
        "modalities":   []string{"audio", "text"},
        "voice":        "alloy",
        "instructions": "You are a helpful assistant.",
    },
})
```

## Send audio

```go
// pcmBytes is raw 16-bit PCM at 24 kHz mono
err = session.SendAudio(ctx, pcmBytes)
```

## RealtimeMessage

Every frame from the server is a `meshapi.RealtimeMessage`. Exactly one of the fields below is non-zero per message:

| Field   | Type             | When set                                                |
| ------- | ---------------- | ------------------------------------------------------- |
| `Audio` | `[]byte`         | Binary frame — raw PCM audio                            |
| `Text`  | `string`         | Raw JSON string for text frames                         |
| `Event` | `map[string]any` | Parsed JSON map for text frames (check `Event["type"]`) |

Audio and text frames are mutually exclusive. Text frames always populate both `Text` and `Event` (unless the frame is not valid JSON, in which case only `Text` is set).

## Receive frames

`Receive` blocks until the next frame arrives. Context cancellation unblocks it immediately:

```go
msg, err := session.Receive(ctx)
if err == io.EOF {
    fmt.Println("session closed")
} else if err != nil {
    log.Fatal(err)
}

if msg.Audio != nil {
    // binary audio frame — play or buffer
    playAudio(msg.Audio)
} else if msg.Event != nil {
    fmt.Println("event type:", msg.Event["type"])
}
```

## Events channel (concurrent pump)

For concurrent send/receive, use `Events` to pump frames into a channel:

```go
msgCh, errCh := session.Events(ctx)

for msg := range msgCh {
    switch {
    case msg.Audio != nil:
        playAudio(msg.Audio)
    case msg.Event != nil:
        switch msg.Event["type"] {
        case "response.text.delta":
            fmt.Print(msg.Event["delta"])
        case "response.done":
            fmt.Println("\n[done]")
        case "error":
            log.Printf("server error: %v", msg.Event)
        }
    }
}

if err := <-errCh; err != nil {
    log.Fatal(err)
}
```

## Full voice agent example

```go
session, _ := client.Realtime.Connect(ctx, meshapi.RealtimeConnectParams{
    Model: "openai/gpt-realtime-mini",
})
defer session.Close()

// Configure
session.Send(ctx, map[string]any{
    "type": "session.update",
    "session": map[string]any{
        "type":       "realtime",
        "modalities": []string{"audio", "text"},
        "voice":      "alloy",
    },
})

// Stream microphone audio
go func() {
    for chunk := range micCh {
        session.SendAudio(ctx, chunk)
    }
    session.Send(ctx, map[string]any{"type": "input_audio_buffer.commit"})
    session.Send(ctx, map[string]any{"type": "response.create"})
}()

// Play response audio
msgCh, _ := session.Events(ctx)
for msg := range msgCh {
    if msg.Audio != nil {
        speaker.Write(msg.Audio)
    }
}
```

## Error handling

Server errors arrive as a `*meshapi.RealtimeError` from `Receive` or on the `errCh` returned by `Events`:

```go
msg, err := session.Receive(ctx)
if err != nil {
    var re *meshapi.RealtimeError
    if errors.As(err, &re) {
        fmt.Println("code:", re.Code)    // "invalid_api_key", "insufficient_quota", …
        fmt.Println("message:", re.Message)
    }
}
```

## Supported models

| Model ID                   |
| -------------------------- |
| `openai/gpt-realtime-mini` |
| `openai/gpt-realtime`      |
| `openai/gpt-realtime-2`    |