TypeScript SDK

Updated July 2026

The official @qalyb/sdk package — fully typed, zero runtime deps, and it runs anywhere modern JavaScript runs (Node 18+, Bun, Deno, Cloudflare Workers, browsers via your backend). Everything you can do in the dashboard, from your own code.

Install

One package, no runtime dependencies — use whichever package manager you already have:

bash
# npm
npm install @qalyb/sdk

# pnpm
pnpm add @qalyb/sdk

# yarn
yarn add @qalyb/sdk

Initialize

Pass your API key explicitly. The client validates every response with Zod, so type-narrowing works without unsafe casts.

qalyb-client.ts · ts
import { Qalyb } from '@qalyb/sdk';

const qalyb = new Qalyb({
  apiKey: process.env.QALYB_API_KEY!,
  // Optional overrides:
  // baseUrl: 'https://api.qalyb.ai',
  // fetch: customFetch, // any fetch-compatible impl
});

Models

The model namespaces give you direct access to TTS, STT, LLM, and the voice catalog — no agents required.

qalyb.tts.create

tts.ts · ts
import fs from 'node:fs';

const audio = await qalyb.tts.create({
  text: 'Marhaba! Welcome to Qalyb.',
  voiceId: 'voice_amal_ar',
  speed: 1.0,
  stability: 0.5,
  similarity: 0.75,
  format: 'mp3',
});

fs.writeFileSync('hello.mp3', Buffer.from(audio));

qalyb.stt.create

stt.ts · ts
import fs from 'node:fs';

const result = await qalyb.stt.create({
  audio: fs.readFileSync('./call.wav'),
  audioMimeType: 'audio/wav',
  language: 'ar',
});

console.log(result.text);
console.log(result.segments);

qalyb.llm.chat

llm.ts · ts
const reply = await qalyb.llm.chat({
  messages: [
    { role: 'system', content: 'You are a friendly receptionist.' },
    { role: 'user',   content: 'Can I book an appointment?' },
  ],
  temperature: 0.4,
});

console.log(reply.message.content);
console.log(reply.usage.promptTokens, reply.usage.completionTokens);

qalyb.voices

voices.ts · ts
// List
const { voices } = await qalyb.voices.list();

// Get one
const amal = await qalyb.voices.get('voice_amal_ar');

// Clone
const cloned = await qalyb.voices.clone({
  audio: fs.readFileSync('./brand-sample.wav'),
  name: 'Acme Brand Voice',
  language: 'ar',
  gender: 'female',
  consent: true,
});

Agents

The qalyb.agents namespace covers the full agent lifecycle plus call control. Outbound and web calls are scoped under the agent that runs them.

agents.ts · ts
// Create
const agent = await qalyb.agents.create({
  name: 'Bookings Concierge',
  personaId: 'p47e2c12-8b8d-4f8e-9b16-7c1a8f9c3a01',
  description: 'Books appointments at Acme Clinic.',
  instructions: {
    systemPrompt: 'You are a friendly receptionist...',
    greeting: 'Marhaba! Welcome to Acme Clinic.',
  },
});

// CRUD
const { agents } = await qalyb.agents.list();
const fresh = await qalyb.agents.get(agent.id);
await qalyb.agents.update(agent.id, { description: 'Updated copy.' });
await qalyb.agents.delete(agent.id);

// Place an outbound call
const call = await qalyb.agents.startCall(agent.id, {
  phoneNumber: '+971501234567',
  variables: { customer_name: 'Layla' },
});

// Start a web call (returns LiveKit credentials for the browser)
const session = await qalyb.agents.startWebCall(agent.id, {
  variables: { customer_name: 'Layla' },
});

// List recent calls for the agent
const { calls } = await qalyb.agents.listCalls(agent.id, { limit: 20 });

// Top-level call lookup (works for any call id)
const detail = await qalyb.calls.get(call.id);

Error handling

Every API error is thrown as a QalybApiError with a typed status, code, and human-readable message. Anything else is a network or runtime error and should bubble up.

ts
import { Qalyb, QalybApiError } from '@qalyb/sdk';

try {
  await qalyb.agents.startCall('agt_abc123', {
    phoneNumber: '+1',
  });
} catch (err) {
  if (err instanceof QalybApiError) {
    console.error(err.status, err.code, err.message);
  } else {
    throw err;
  }
}

Module map

NamespaceMethodsEndpoints
qalyb.ttscreatePOST /v1/tts
qalyb.sttcreatePOST /v1/stt
qalyb.llmchatPOST /v1/llm/chat
qalyb.voiceslist, get, cloneGET /v1/voices, GET /v1/voices/:id, POST /v1/voices/clone
qalyb.agentscreate, list, get, update, delete, startCall, startWebCall, listCalls/v1/agents, /v1/agents/:id/calls, /v1/agents/:id/web-call
qalyb.callsgetGET /v1/calls/:id

Every method mirrors the REST surface documented in the API reference.

Was this guide helpful?