# Managed Cloud

> The fastest path to a live voice agent: get a key, install the SDK, and originate a call. You run no infrastructure.

This is the shortest path from zero to a voice agent that talks to a real
caller. On managed cloud, FirstByte runs the media engine, the SIP trunking
edge, and the agent runtime for you. You get an API key and make one call. In a
few minutes you will have one of two results: an **AI agent that phones a
number over the PSTN**, or a **click-to-talk voice agent in the browser**.

> **NOTE:**
> Managed cloud is a single production environment. There is no separate sandbox
> tenant. Test against your own numbers and a low-volume trunk before you scale
> up. If you need an isolated stack, see
> [On-Prem / Self-Hosted](/modalities/voice/quickstart/on-prem).

## Before you start

  1. **Create a workspace and API key**
Sign in at the portal, **portal.firstbyte.fast**. Create (or pick) a
    workspace. Mint an MCP access key (`mpk_…`) under
    **Settings → API Keys → MCP access keys** with the scope
    `mcp:telephony:write`. Add `mcp:telephony:read` if your code also reads
    call data. Keep the key server-side.

    Your workspace automatically gets a per-tenant SIP edge
    (`<workspace-id>.sip.firstbyte.fast`) and a browser media endpoint
    (`<workspace-id>.webrtc.firstbyte.fast`).
  2. **Configure a trunk and a number (for phone calls)**
In the Voice AI console at **voiceai.firstbyte.fast**, add an outbound SIP
    trunk. Claim or bring a phone number. Note the trunk id. You pass it as
    `trunkId`. Skip this step if you only want the browser agent below.
  3. **Build an agent**
Still in **voiceai.firstbyte.fast**, create a voice agent and open its
    **Config** tab:

    1. Pick a speech-to-speech or ASR→LLM→TTS provider.
    2. Give the agent a system prompt.
    3. Note the agent's id (e.g. `appointment-reminder`).

    You reference the agent by id when you originate. See
    [Build a voice agent](/modalities/voice/build/voice-agents) for the full
    walkthrough.

## Install the SDK

  
```bash
npm install clutchcall_connect
```
  
  
```bash
pip install clutchcall-sdk
```
  

Put your key in the environment, so it never lands in source:

```bash
export CLUTCHCALL_CREDENTIALS="mpk_…"
```

## Path A — an AI agent on a phone call

Originate an outbound call and attach a server-side agent. When the callee
answers, the engine connects the audio bridge to your agent automatically. Your
process never touches the media. This is the whole app.

  1. **Originate with an agent attached**
Pass `agent` to `originate()`. The engine attaches the runtime on answer.
  2. **Wait for connect or failure**
The control plane is request/response. Poll `calls.get()` until the call
    leaves the ringing states.

  
```ts
import { Voice } from "clutchcall_connect/voice";

const v = new Voice({
  baseUrl: "https://engine.firstbyte.fast",
  apiKey:  process.env.CLUTCHCALL_CREDENTIALS!,
  orgId:   "org_abc",
});

const call = await v.calls.originate({
  to:      "+15551234567",
  from:    "+15558675309",
  trunkId: "trunk_main",
  agent:   "appointment-reminder",
  ringTimeoutSec: 25,
});

for (;;) {
  const c = await v.calls.get({ sid: call.sid });
  if (c.status === "in_progress") { console.log("agent is live on", call.sid); break; }
  if (c.status === "failed" || c.status === "no_answer") { console.log("ended:", c.status); break; }
  await new Promise((r) => setTimeout(r, 500));
}
```
  
  
```python
import os, time
from clutchcall.voice import Voice

v = Voice(
    base_url="https://engine.firstbyte.fast",
    api_key=os.environ["CLUTCHCALL_CREDENTIALS"],
    org_id="org_abc",
)

call = v.calls.originate(
    to="+15551234567", from_="+15558675309", trunk_id="trunk_main",
    agent="appointment-reminder", ring_timeout_sec=25,
)

while True:
    c = v.calls.get(sid=call.sid)
    if c.status == "in_progress":
        print("agent is live on", call.sid); break
    if c.status in ("failed", "no_answer"):
        print("ended:", c.status); break
    time.sleep(0.5)
```
  

> **TIP:**
> The agent drives both legs of the conversation. Turn-taking, barge-in, and tool
> calls all run server-side. Tune that behavior in
> [Turn detection](/modalities/voice/concepts/turn-detection) and
> [Tool calling](/modalities/voice/runtime/tool-calling).

## Path B — a browser voice agent

This path needs no phone number. Capture the mic in the browser. Publish the
audio as encoded Opus. Play the agent's reply back. Media rides QUIC end to
end, so this path has no WebRTC transport and no SFU. Use a **browser-scoped**
token: a short-lived token that your backend mints for one call and hands to
the page. No console screen mints one. See the
[minimal backend](/modalities/voice/recipes/browser-agent#minimal-backend) in
the browser agent recipe. Never put your `mpk_…` server key in the page.

  1. **Originate a browser-legged call**
Get a `sid` from the control plane.
  2. **Start playback**
Create and `start()` an `OpusPlayer` for the downlink.
  3. **Attach as the caller**
`attachCaller()` subscribes downlink and publishes uplink.
  4. **Capture the mic**
`captureMicrophone()` forwards encoded frames onto the uplink.

```ts
import { Voice } from "clutchcall_connect/voice";
import { captureMicrophone, OpusPlayer } from "clutchcall_connect/moqt";

const v = new Voice({
  baseUrl: "https://engine.firstbyte.fast",
  apiKey:  BROWSER_SCOPED_TOKEN,   // from your backend: one call, short TTL
  orgId:   "org_abc",
});

async function talkToAgent() {
  const call = await v.calls.originate({ agent: "web-concierge" });

  const ctx = new AudioContext();
  const player = new OpusPlayer(ctx, { sampleRate: 48000, channels: 1 });
  await player.start();

  const bridge = await v.audioBridge.attachCaller(call.sid, {
    codec: "opus",
    onDownlink: (frame, tsUs) => player.push(tsUs, frame),
  });

  const mic = await captureMicrophone(
    { write: (tsUs, frame) => bridge.publishUplink(frame, tsUs) } as any,
    { audioConstraints: { echoCancellation: true, autoGainControl: true, noiseSuppression: true } },
  );

  return async function hangUp() {
    mic.stop(); player.close();
    await bridge.close();
    await call.hangup();
  };
}
```

> **NOTE:**
> `captureMicrophone` runs the browser's own echo-cancellation, gain control, and
> noise suppression. It then diverts the **encoded** Opus frames onto the uplink
> track. Raw PCM never crosses the wire. This works today in Chromium-based
> browsers. Safari and UDP-blocked networks fall back to a WebSocket/WebRTC media
> leg. See
> [Browser compatibility](/modalities/voice/transport-web/browser-compatibility).

## Next steps

  - **[Build a voice agent](/modalities/voice/build/voice-agents)** — Prompt, providers, and tools for the agent that you attached above.
  - **[Recipes](/modalities/voice/recipes/outbound-sales)** — End-to-end worked examples: outbound sales, browser agent, human handoff.
  - **[Calls API](/modalities/voice/api/calls)** — Full signatures for `originate`, `get`, `transfer`, and `hangup`.
  - **[Outbound telephony](/modalities/voice/transport-telephony/outbound)** — Trunks, numbers, and the dialer behind Path A.
