Skip to main content

Install

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

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

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:
// default
const client = new YotelClient({ apiKey: "..." });

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

Error handling

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.
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.
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 for the full flow reference.

Source

github.com/kamal-zetta/dialer/tree/main/sdks/typescript