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

# TypeScript SDK

> Zero-dep TypeScript client for the Yotel public API.

## Install

```bash theme={null}
npm install @yotel/client
```

Runs in Node 18+, Deno, Bun, and modern browsers (though you
probably don't want to call the API from a browser — the key would
be exposed).

## Quickstart

```typescript theme={null}
import { YotelClient } from "@yotel/client";

const client = new YotelClient({
  apiKey: process.env.YOTEL_KEY!,
});

// Create campaign
const c = await client.campaigns.create({
  name: "Q4 outreach",
  dial_mode: "predictive",
  predictive_target_abandon: 0.02,
});

// Push lead
const lead = await client.leads.create({
  campaign_id: c.id,
  phone: "9876543210",
  name: "Priya",
  email: "priya@example.com",
});

// Bulk push
const result = await client.leads.bulk({
  campaign_id: c.id,
  leads: [
    { phone: "9876543211" },
    { phone: "9876543212", name: "Arjun" },
  ],
});
console.log(result.valid, "added,", result.duplicates, "skipped");
```

## Verifying webhooks

```typescript theme={null}
import express from "express";
import { verifyWebhook, InvalidSignature } from "@yotel/client";

const app = express();

app.post(
  "/webhooks/yotel",
  express.raw({ type: "application/json" }),
  (req, res) => {
    try {
      const event = verifyWebhook({
        rawBody: req.body,
        headers: req.headers,
        signingSecret: process.env.YOTEL_WEBHOOK_SECRET!,
      });

      if (event.event_type === "call.ended") {
        const callId = event.data.call_id;
        // ...
      }

      res.status(200).send();
    } catch (e) {
      if (e instanceof InvalidSignature) {
        return res.status(401).send();
      }
      throw e;
    }
  },
);
```

## Rate-limit handling

The client auto-retries 429s up to 3 times, respecting `Retry-After`:

```typescript theme={null}
// default
const client = new YotelClient({ apiKey: "..." });

// disable
const client = new YotelClient({ apiKey: "...", retryOn429: false });
```

## Error handling

```typescript theme={null}
import {
  YotelClient,
  AuthenticationError,
  RateLimitError,
  ValidationError,
  APIError,
} from "@yotel/client";

try {
  await client.leads.create({ campaign_id: "c-1", phone: "bad" });
} catch (e) {
  if (e instanceof ValidationError) {
    console.log(e.errors);
  } else if (e instanceof RateLimitError) {
    console.log("retry after", e.retryAfter);
  } else if (e instanceof AuthenticationError) {
    // ...
  } else if (e instanceof APIError) {
    console.log(e.statusCode, e.detail);
  }
}
```

## Types

Every request + response is typed. Hover in VS Code for docs on each
field.

```typescript theme={null}
import type { Campaign, Lead, PredictiveStats } from "@yotel/client";
```

## OAuth helpers

For partner apps that act on behalf of individual Yotel users, the
SDK ships PKCE-ready OAuth helpers — works in browser + Node.

```typescript theme={null}
import { AuthorizationCodeFlow, generateCodeVerifier } from "@yotel/client";

const flow = new AuthorizationCodeFlow({
  clientId: "sfdc_yotel",
  clientSecret: process.env.YOTEL_CLIENT_SECRET!,
  redirectUri: "https://app.example/cb",
  scopes: ["campaigns:read", "openid", "offline_access"],
});
const verifier = generateCodeVerifier();
const url = await flow.buildAuthorizeUrl({ state: "csrf-1", codeVerifier: verifier });
// ... redirect user, callback handler ...
const tokens = await flow.exchangeCode({ code, codeVerifier: verifier });
const next = await tokens.refresh(); // rotated refresh_token
```

`JWTBearerFlow` is Node-only (it signs with `node:crypto`). Browser
bundles that only use the authorization-code flow won't include any
Node-specific imports.

See [OAuth 2.0](/authentication/oauth) for the full flow reference.

## Recordings

Access call recordings — signed URLs, raw bytes, or streamed.

```typescript theme={null}
// Get a signed URL (7-day expiry)
const recording = await client.recordings.getSignedUrl("call_id");
console.log(recording.url, recording.stereo);

// Download to disk (Node.js only)
await client.recordings.download("call_id", "recording.wav");

// Load into memory
const audio = await client.recordings.fetchBytes("call_id");

// Stream to a writable (Node.js only)
import { createWriteStream } from "fs";
await client.recordings.streamTo("call_id", createWriteStream("out.wav"));
```

<Note>
  `getSignedUrl` requires `calls:read` scope. `fetchBytes`, `download`,
  and `streamTo` require the separate `recordings:download` scope.
</Note>

See [Recordings API](/api-reference/recordings) for response details.

## Voice agents

Full CRUD for voice agent routing aliases.

```typescript theme={null}
// Create
const va = await client.voiceAgents.create({
  name: "Support bot",
  ws_url: "wss://ai.example.com/ws",
  max_concurrent: 50,
});

// List / Get
const all = await client.voiceAgents.list();
const one = await client.voiceAgents.get(va.id);

// Update (PATCH)
await client.voiceAgents.update(va.id, { max_concurrent: 100 });

// Archive (soft delete) or hard delete
await client.voiceAgents.archive(va.id);
await client.voiceAgents.delete(va.id);

// Tenant default
await client.voiceAgents.setAsDefault(va.id);
const defaultId = await client.voiceAgents.getDefault();
```

See [Voice agents CRUD](/api-reference/voice-agents/crud) and
[Voice agent defaults](/api-reference/voice-agents/defaults).

## AI sessions

Control active voice AI sessions with typed verb methods.

```typescript theme={null}
// Transfer call to a human agent
await client.aiSessions.transfer("call_abc", {
  destinationType: "agent",
  destination: "agent_xyz",
  callbackToken: "yt_cb_...",
});

// Hang up
await client.aiSessions.hangup("call_abc", {
  callbackToken: "yt_cb_...",
});

// Set disposition
await client.aiSessions.setDisposition("call_abc", {
  disposition: "interested",
  notes: "Wants premium plan",
  callbackToken: "yt_cb_...",
});

// Get current call state
const state = await client.aiSessions.getCallState("call_abc", {
  callbackToken: "yt_cb_...",
});
```

All 25 control verbs are available as typed methods:
`transfer`, `hangup`, `log`, `mute`, `unmute`, `hold`, `unhold`,
`sendDtmf`, `playAudio`, `setDisposition`, `recordingPause`,
`recordingResume`, `getCallState`, `conferenceStart`,
`conferenceAdd`, `conferenceRemove`, `conferenceLeave`,
`requestSupervisor`, `whisper`, `barge`, `monitorStart`,
`monitorStop`, `setLeadField`, `setLeadStatus`,
`scheduleCallback`.

See [AI sessions](/api-reference/ai-sessions) and the
[Control API reference](/voice-agents/control-api) for verb parameters.

## Additional error types

Beyond the [base errors](#error-handling), voice agent and AI session
operations can throw:

```typescript theme={null}
import {
  NoVoiceAgentConfigured,        // 424 — no agent resolved in hierarchy
  VoiceAgentInUse,               // 409 — active sessions prevent deletion
  MaxConcurrentExceeded,         // 429 — concurrency limit reached
  ConferenceMembershipRequired,  // 403 — whisper/barge needs conference
  AISessionTerminated,           // 410 — call already ended
} from "@yotel/client";
```

See [Error reference](/api-reference/error-reference) for details.

## Source

[github.com/kamal-zetta/dialer/tree/main/sdks/typescript](https://github.com/kamal-zetta/dialer/tree/main/sdks/typescript)
