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

# SDKs

> Official clients for TypeScript and Dart, generated from this API's own OpenAPI document.

Two official SDKs, both covering every `/v1` endpoint:

|                               | Install                     | Runs on                           |
| ----------------------------- | --------------------------- | --------------------------------- |
| **TypeScript and JavaScript** | `npm install wixzel-phone`  | Node 20+, browsers, edge runtimes |
| **Dart and Flutter**          | `dart pub add wixzel_phone` | Dart 3.5+, Flutter including web  |

Both retry rate limits, page through lists, send an idempotency key on the
paths that spend money, and raise errors carrying the API's stable `code`,
the `request_id` to quote and the `doc_url` to read. A test in each compares
its method table against the [OpenAPI document](/api-reference) in both
directions, so an endpoint cannot go unwrapped and a method cannot claim an
endpoint that does not exist.

<Note>
  There is no Python SDK yet. [Integrate](/integrate) has a fifteen-line client
  in Node and in Python for anyone who would rather not add a dependency.
</Note>

## Quickstart

<CodeGroup>
  ```ts TypeScript theme={null}
  import { WixzelPhone } from 'wixzel-phone';

  const client = new WixzelPhone({ apiKey: process.env.WIXZEL_API_KEY! });

  const agent = await client.agents.create({
    name: 'Support',
    system_prompt: 'You are a concise support agent.',
    opening_message: 'Hi, how can I help?',
    voice: {
      stt: { model: 'deepgram/nova-3' },
      llm: { model: 'openrouter/gpt-4o-mini' },
      tts: { model: 'elevenlabs/eleven_turbo_v2_5' },
    },
  });

  const call = await client.calls.create({ to: '+14155551234', agent_id: agent.id });
  console.log(call.id, call.status);
  ```

  ```dart Dart theme={null}
  import 'package:wixzel_phone/wixzel_phone.dart';

  final client = WixzelPhone(apiKey: 'wv_live_…');

  final agent = await client.agents.create(CreateAgent(
    name: 'Support',
    systemPrompt: 'You are a concise support agent.',
    openingMessage: 'Hi, how can I help?',
    voice: VoiceConfig.composed(
      stt: const SttConfig(model: 'deepgram/nova-3'),
      llm: const LlmConfig(model: 'openrouter/gpt-4o-mini'),
      tts: const TtsConfig(model: 'elevenlabs/eleven_turbo_v2_5'),
    ),
  ));

  final call = await client.calls.create(
    CreateCall(to: '+14155551234', agentId: agent.id),
  );
  print('${call.id} ${call.status.value}');
  client.close();
  ```
</CodeGroup>

Both SDKs name their methods the same way: `agents.create`, `calls.list`,
`sipTrunks.status`, `billing.createTopup`. Twelve resources, 56 methods.

## Pagination

Lists are cursor-paginated. Iterate the page to walk every page; the cursor
and your filters are carried along.

<CodeGroup>
  ```ts TypeScript theme={null}
  for await (const call of await client.calls.list({ status: 'completed', limit: 100 })) {
    console.log(call.id, call.duration_seconds);
  }

  // or page by page
  let page = await client.leads.list({ tag: 'clinic' });
  while (page) {
    console.log(page.data.length, page.hasMore);
    page = await page.nextPage();
  }
  ```

  ```dart Dart theme={null}
  final calls = await client.calls.list(status: CallStatus.completed, limit: 100);
  await for (final call in calls.autoPaging()) {
    print('${call.id} ${call.durationSeconds}');
  }

  // or page by page
  var page = await client.leads.list(tag: 'clinic');
  while (page != null) {
    print('${page.data.length} ${page.hasMore}');
    page = await page.nextPage();
  }
  ```
</CodeGroup>

## Errors

Match on `code`. The message is written for people and may be reworded.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { WixzelError, WixzelConnectionError } from 'wixzel-phone';

  try {
    await client.calls.create({ to, agent_id });
  } catch (err) {
    if (WixzelError.is(err)) {
      if (err.is('insufficient_credits')) console.log('balance:', err.balance);
      console.log(err.status, err.code, err.requestId, err.docUrl);
    } else if (err instanceof WixzelConnectionError) {
      // No answer, even after retries.
    }
  }
  ```

  ```dart Dart theme={null}
  try {
    await client.calls.create(CreateCall(to: to, agentId: agentId));
  } on WixzelException catch (e) {
    if (e.code == 'insufficient_credits') print('balance: ${e.balance}');
    print('${e.statusCode} ${e.code} ${e.requestId} ${e.docUrl}');
  } on WixzelConnectionException catch (e) {
    // No answer, even after retries.
    print(e.message);
  }
  ```
</CodeGroup>

## Idempotency

`calls.create` and `billing.createTopup` require an
[idempotency key](/idempotency). The SDKs generate one per call and reuse it
across their own retries, so a network timeout never becomes two phone calls.
Pass your own to make a retry from your side safe too.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { lastResponse } from 'wixzel-phone';

  const call = await client.calls.create(
    { to, agent_id },
    { idempotencyKey: `order-${orderId}` },
  );
  if (lastResponse(call)?.idempotentReplay) {
    console.log('the server had already placed this call');
  }
  ```

  ```dart Dart theme={null}
  final result = await client.calls.createWithResponse(
    CreateCall(to: to, agentId: agentId),
    idempotencyKey: 'order-$orderId',
  );
  if (result.idempotentReplay) {
    print('the server had already placed this call');
  }
  ```
</CodeGroup>

## Retries

The two SDKs share one policy:

* **`429`**: retried after `Retry-After`, capped at ten seconds, up to
  `maxRetries` (default 2). Rate-limited requests are [never charged](/rate-limits).
* **Network failures, timeouts and `502`–`504`**: retried only for `GET` and
  for requests carrying an idempotency key. `DELETE`, `PATCH` and unkeyed
  `POST` (hanging up, starting or pausing a campaign, testing a trunk,
  rotating a key) are never repeated when the outcome is unknown.
* **`500` and other `4xx`**: never retried.

## Pinning a version

<CodeGroup>
  ```ts TypeScript theme={null}
  const client = new WixzelPhone({ apiKey, apiVersion: '2026-09-01' });
  ```

  ```dart Dart theme={null}
  final client = WixzelPhone(apiKey: apiKey, apiVersion: '2026-09-01');
  ```
</CodeGroup>

Sends the `Wixzel-Version` header, so an upgrade is something you do rather
than something that happens to you.

## Options

|                         | Default                        |                                           |
| ----------------------- | ------------------------------ | ----------------------------------------- |
| `apiKey`                | required                       | `wv_live_…` or `wv_test_…`                |
| `baseUrl`               | `https://api.phone.wixzel.com` | For a self-hosted API                     |
| `apiVersion`            | none                           | Date pin, as above                        |
| `timeoutMs` / `timeout` | 30 s                           | Per attempt                               |
| `maxRetries`            | 2                              | Retries after the first attempt           |
| `fetch` / `httpClient`  | the platform's                 | Injectable, for tests and custom runtimes |
| `defaultHeaders`        | none                           | Sent on every request                     |

Both expose an escape hatch, `client.request(method, path, …)`, that reaches
an endpoint the SDK does not model yet with the same auth, retries and error
handling.

## In the browser and in Flutter

<Warning>
  A live key in a page or a shipped app is a key anyone can read. Call your own
  backend from the client, and let the backend hold the key.
</Warning>

The API's CORS policy does not yet expose `Retry-After`, `X-Wixzel-Balance`
or `Idempotent-Replay` to page scripts, so those read as empty in a browser.
Everything else works.

## Source

* npm: [`wixzel-phone`](https://www.npmjs.com/package/wixzel-phone) · [source](https://github.com/aqeelshamz/wixzel-phone-sdks/tree/main/packages/sdk-ts)
* pub.dev: [`wixzel_phone`](https://pub.dev/packages/wixzel_phone) · [source](https://github.com/aqeelshamz/wixzel-phone-sdks/tree/main/packages/sdk-dart)
* Issues and pull requests: [aqeelshamz/wixzel-phone-sdks](https://github.com/aqeelshamz/wixzel-phone-sdks/issues)
* Building with an AI agent instead? See the [MCP server](/mcp-server).
