# Go

> The Voice SDK for Go. Originate, transfer, and hang up calls, bridge audio, and attach an AI agent from one typed client.

The Go Voice SDK lives in package `github.com/clutchcall/clutchcall-sdk/go/pkg/voice`. It gives you the
same two primitives as every other language binding: **Calls** (the control
plane) and **AudioBridge** (the data plane). It adds **Agents** to bind a
server-side AI agent to a live call. Every operation keys off one identifier,
a call's `Sid`.

The two planes have very different runtime requirements in Go. Know this
before you start:

> **NOTE:**
> **Calls and Agents are pure Go.** They use the control-plane API over plain
> `net/http`, so they build and run anywhere Go does, with no C toolchain.
> 
> **AudioBridge is a cgo binding.** It moves encoded audio over MoQT through the
> SDK's native transport. To build code that touches `AudioBridge`, you need
> `CGO_ENABLED=1`, a C toolchain, and the SDK's native audio shared library on
> your linker/loader path. If you only place and manage calls, or attach an AI
> agent and let the engine own the media, you never touch the data plane. In
> that case you can build a pure-Go binary.

## Install

```bash
go get github.com/clutchcall/clutchcall-sdk/go
```

```go
import "github.com/clutchcall/clutchcall-sdk/go/pkg/voice"
```

The AudioBridge also uses the core package at `github.com/clutchcall/clutchcall-sdk/go/pkg` (the
native MoQT client). You rarely import it directly.

## Construct the client

`voice.New` returns a `*Voice` scoped to one org. All three arguments are
required. The function returns an error if any argument is empty. Construct
the client once and reuse it. It is safe to share across goroutines.

- **`baseURL`** (`string`, required) — Control-plane API host, for example `https://engine.firstbyte.fast`. The SDK trims a trailing slash.
- **`apiKey`** (`string`, required) — Your API key. The SDK sends it as `Authorization: Bearer <key>` on every control-plane request.
- **`orgID`** (`string`, required) — The org (workspace) id that scopes every operation.

```go
package main

import (
	"log"
	"os"

	"github.com/clutchcall/clutchcall-sdk/go/pkg/voice"
)

func main() {
	v, err := voice.New(
		"https://engine.firstbyte.fast",
		os.Getenv("CLUTCHCALL_API_KEY"),
		"org_123",
	)
	if err != nil {
		log.Fatal(err)
	}

	// The three sub-clients are accessors (methods), not fields:
	calls  := v.Calls()
	agents := v.Agents()
	bridge := v.AudioBridge()
	_ = bridge
}
```

The `Voice` struct exposes a few fields you can set after construction:

- **`RelayHost`** (`string`, default `relay.firstbyte.fast`) — Host for the AudioBridge MoQT session. Point it at your workspace relay.
- **`HTTP`** (`*http.Client`, default `http.DefaultClient`) — Override to set timeouts, a proxy, or custom transport for control-plane calls.

```go
v.RelayHost = "relay.firstbyte.fast"
v.HTTP = &http.Client{Timeout: 10 * time.Second}
```

## Calls — the control plane

`v.Calls()` returns a `*Calls`. Every method maps to one control-plane RPC and
returns a typed result or an error.

### Originate

`Originate` dials `To` from `From` over the trunk you name. If you pass
`Agent`, the engine attaches that server-side voice agent automatically when
the far end answers. You do not open an AudioBridge yourself.

- **`To`** (`string`, required) — Destination in E.164, e.g. `+15551234567`.
- **`From`** (`string`, required) — Caller-ID presented on the outbound leg, in E.164. It must be a number your trunk is allowed to present.
- **`TrunkID`** (`string`, required) — The trunk to originate on. See [SIP trunking](/modalities/voice/transport-telephony/sip-trunking).
- **`Agent`** (`string`) — Optional agent id. When set, the engine binds this server-side voice agent on answer and wires the audio bridge end-to-end.
- **`RingTimeoutSec`** (`int`, default `30`) — The number of seconds to ring before the attempt stops. If you leave the zero value, the SDK sends `30`.

```go
call, err := v.Calls().Originate(voice.OriginateArgs{
	To:      "+15551234567",
	From:    "+15558675309",
	TrunkID: "trunk_main",
	Agent:   "healthcare-assistant", // optional
})
if err != nil {
	log.Fatal(err)
}
log.Println(call.Sid, call.Status) // "cs_…", "dialing"
```

`Originate` returns a `*Call`, which embeds `CallData`:

- **`Sid`** (`string`) — The universal call key.
- **`Status`** (`string`) — Lifecycle state, e.g. `dialing`, `in_progress`, `completed`.
- **`To`** (`string`) — Destination number.
- **`From`** (`string`) — Caller-ID.
- **`StartedAt`** (`string`) — Start timestamp.
- **`TrunkID`** (`string`) — Trunk the call was placed on (omitted when empty).
- **`Agent`** (`string`) — Bound agent id, if any (omitted when empty).

### Get

Fetch the current state of a call by `sid`.

```go
call, err := v.Calls().Get("cs_abc123")
if err != nil {
	log.Fatal(err)
}
log.Println(call.Status)
```

### Transfer

`Call.Transfer` moves a live call. Pass **exactly one** of `To` (hand off to a
PSTN number) or `Agent` (re-attach to a different server-side agent). If you
pass both, or neither, the SDK returns an error before it sends any RPC.

```go
// Warm-transfer the caller to a human on the PSTN:
if err := call.Transfer(voice.TransferArgs{To: "+15558675309"}); err != nil {
	log.Fatal(err)
}

// …or re-point the call at a different AI agent:
err = call.Transfer(voice.TransferArgs{Agent: "billing-specialist"})
```

See [AI ↔ human handoff](/modalities/voice/transport-telephony/ai-human-handoff)
for the end-to-end warm-handoff flow.

### Hangup

```go
if err := call.Hangup(); err != nil {
	log.Fatal(err)
}
```

## Agents — attach an AI agent

`v.Agents().Attach` binds a server-side voice agent to a call that is already
up. Use it when you originated a bare call (no `Agent`) and now want the engine
to take over the conversation. Also use it to attach an agent to an inbound
call your backend just learned about.

```go
if err := v.Agents().Attach(call.Sid, "healthcare-assistant"); err != nil {
	log.Fatal(err)
}
```

After the attach, the engine drives ASR/LLM/TTS (or a speech-to-speech
provider) and owns both audio legs. You do not open an AudioBridge. Configure
what the agent *does* in the [runtime](/modalities/voice/runtime/overview)
docs.

## AudioBridge — the data plane

Use the AudioBridge only when you want the **raw encoded frames** of a call in
your Go process — for example, to feed a custom media pipeline or a runtime
the engine does not natively host. When an agent is attached, you do not need
it.

`v.AudioBridge().Attach` subscribes to the caller's audio at the
`voice/<sid>/uplink` track and opens a publisher on `voice/<sid>/downlink` for
audio you send back. (See
[sessions, calls, tracks & streams](/modalities/voice/concepts/sessions-calls-tracks-streams)
for the track model.)

- **`OnUplink`** (`func(frame []byte, timestampUs uint64)`, required) — The SDK calls this for every inbound frame from the caller. `frame` is the encoded payload. `timestampUs` is the frame's microsecond timestamp. This callback is required — `Attach` returns an error without it.
- **`Codec`** (`voice.Codec`, default `CodecOpus`) — One of `CodecOpus`, `CodecPCM16`, `CodecG711ULaw`, `CodecG711ALaw`. Voice is audio-only. There is no video codec here.
- **`SampleRate`** (`uint32`, default `48000`) — Sample rate of the downlink track.
- **`Channels`** (`uint8`, default `1`) — Channel count (voice is mono).
- **`FrameMs`** (`uint16`, default `20`) — Frame duration in milliseconds.

```go
bridge, err := v.AudioBridge().Attach(call.Sid, voice.AudioBridgeOpts{
	Codec: voice.CodecOpus,
	OnUplink: func(frame []byte, timestampUs uint64) {
		// caller audio arrives here, frame by frame
		process(frame)
	},
})
if err != nil {
	log.Fatal(err)
}
defer bridge.Close()

// Push one encoded frame back to the caller:
bridge.PublishDownlink(reply) // []byte, timestamped for you
```

> **WARNING:**
> `AudioBridge.Attach` (and everything it returns) is the cgo path. It requires
> `CGO_ENABLED=1`, a C toolchain at build time, and the SDK's native audio shared
> library at run time. The rest of this page (Calls, Agents) is pure Go.

`PublishDownlink` stamps each frame with the current time. You give it only
the encoded bytes. Always `Close()` the bridge when you are done. `Close()`
tears down the publisher, the subscription, and the underlying MoQT session.

## Errors

Control-plane methods return a plain `error`. SDK-side validation failures (a
missing field, an invalid `Transfer` combination) come back as `*voice.Error`.
Transport and remote failures show the HTTP status or the control-plane error
message. Handle them the usual Go way:

```go
call, err := v.Calls().Originate(args)
if err != nil {
	var ve *voice.Error
	if errors.As(err, &ve) {
		// client-side validation problem — bad args
	}
	log.Printf("originate failed: %v", err)
	return
}
```

## What the Go surface does *not* include yet

The Go binding is deliberately the core of two primitives plus agents. Some
features from other bindings are **not** in `pkg/voice` today. Use the core
package or another language where noted:

> **NOTE:**
> - **No live call-lifecycle event stream** in `pkg/voice`. `OnUplink` delivers
>   audio, not signalling events. For a native call-event stream (originate,
>   answer, transfer, cleared) use the lower-level core client in `github.com/clutchcall/clutchcall-sdk/go/pkg`,
>   which exposes an event callback over the native QUIC RPC plane. See the
>   [SDK events](/modalities/voice/sdk/events) reference.
> - **No human-agent softphone control channel.** The browser/desktop agent
>   control channel (login, presence, call-offer accept/reject) currently ships in
>   the [JavaScript SDK](/modalities/voice/sdk/javascript) only.
> - **No video.** Voice tracks are audio-only.

## Next steps

  - **[Calls API](/modalities/voice/api/calls)** — The full control-plane contract behind `Calls` — originate, get, transfer, hangup.
  - **[Audio bridge](/modalities/voice/api/transport)** — The MoQT track model and frame semantics the AudioBridge rides on.
  - **[Attach an agent](/modalities/voice/api/agents)** — Bind a server-side AI agent and let the engine own the media.
  - **[Outbound calling](/modalities/voice/transport-telephony/outbound)** — Originate, campaigns, and the paced dialer end to end.
