aethexDocs

TypeScript SDK

Official TypeScript SDK for the AethexAI Voice API. Create and manage voice agents, place phone calls, synthesize speech, transcribe audio, and run realtime WebRTC conversations from Node.js, the browser, and edge runtimes.

Installation

The SDK ships as the aethexai package on npm and requires Node.js 20+ (for the global fetch, Blob, and File APIs) or any modern browser / edge runtime. It bundles dual ESM + CommonJS builds with TypeScript types. Install @roamhq/wrtc as well only if you need realtime WebRTC from Node.js.

bash
npm install aethexai

Quick setup

Import AethexAI and initialise it with your API key. The SDK uses API-key authentication for voice API calls; developer JWT login (DeveloperClient) is only for account and billing. The key falls back to the AETHEX_API_KEY environment variable.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." }); // or set AETHEX_API_KEY

// Verify the connection by listing available voices.
const voices = await client.listVoices({ limit: 5 });
for (const voice of voices) {
  console.log(voice.id, voice.name);
}

Promises, not sync/async

Every method returns a Promise — there is no separate sync and async client. Configure the client with baseURL, a per-request timeout in milliseconds, and an optional custom fetch implementation.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({
  apiKey: "ae_live_...",
  baseURL: "https://api.aethexai.com", // default
  timeout: 30_000,                      // ms
});

const page = await client.listAgents({ limit: 20 });
console.log(page.data);

Clients

The package exposes three clients. AethexAI is the full API-key surface (agents, calls, TTS, transcription, conversations, phone numbers, Twilio accounts, usage, uploads, API keys). Kora is a focused voice client. DeveloperClient is JWT-authenticated for account and billing.

Kora

Kora is the voice-focused client: agents, outbound calls, voices, TTS, transcription, and conversation history without the account-management surface. Its constructor is positional — new Kora(baseURL, apiKey) — and createAgent / triggerCall take positional arguments for the most common fields.

typescript
import { Kora } from "aethexai";
import { readFile } from "node:fs/promises";

const kora = new Kora("https://api.aethexai.com", "ae_live_...");

const voices = await kora.listVoices({ limit: 1 });
const agent = await kora.createAgent(
  "Support Agent",
  "You are a helpful customer support agent.",
  voices.at(0)!.id,
  { first_message: "Hi there! How can I help you today?", language: "english" },
);

const bytes = await readFile("recording.wav");
const result = await kora.transcribe(bytes, { language: "english" });
console.log(result.text);

Developer account & billing

Billing and account endpoints require a developer JWT from the dashboard sign-in flow, not an API key. DeveloperClient carries the bearer token and, when given a refresh token, transparently refreshes once on a 401 and retries. Tokens fall back to AETHEX_DEVELOPER_ACCESS_TOKEN and AETHEX_DEVELOPER_REFRESH_TOKEN.

typescript
import { DeveloperClient } from "aethexai";

const developer = new DeveloperClient({
  accessToken: "eyJhbGciOi...",
  refreshToken: "eyJhbGciOi...", // optional; enables one retry after a 401 refresh
});

const balance = await developer.getBalance();
const plans = await developer.listPlans();
console.log(balance, plans);

Agents

Create and manage voice agents, their tools, and their knowledge base. Write methods return typed models — read agent.id, not agent['id'].

List Agents

List all agents in your tenant. Returns a paginated result with .data, .total, .offset, and .limit.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listAgents({ limit: 20, offset: 0 });
for (const agent of page) {
  console.log(agent.id, agent.name);
}

Create Agent

Create a new voice agent. Write methods return typed models; read agent.id, not agent['id'].

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });
const voices = await client.listVoices({ language: "english", limit: 1 });

const agent = await client.createAgent({
  name: "Support Bot",
  system_prompt: "You are a helpful customer support agent.",
  first_message: "Hi there! How can I help you today?",
  voice_id: voices.at(0)!.id,
  language: "english",
});
console.log(agent.id);

Get Agent

Retrieve a single agent by ID. Returns a typed AgentResponse — use attribute access (agent.id, agent.name).

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const agent = await client.getAgent("8a1b2c3d-4e5f-6789-abcd-ef0123456789");
console.log(agent.id, agent.name, agent.voice_id);

Update Agent

Update an existing agent. Pass only the fields you want to change.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.updateAgent("8a1b2c3d-4e5f-6789-abcd-ef0123456789", {
  name: "Updated Support Bot",
  first_message: "Welcome back!",
});

Delete Agent

Permanently delete an agent by ID.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.deleteAgent("8a1b2c3d-4e5f-6789-abcd-ef0123456789");

Duplicate Agent

Create an exact copy of an existing agent. Returns the new agent as a typed object.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const clone = await client.duplicateAgent("8a1b2c3d-4e5f-6789-abcd-ef0123456789");
console.log(clone.id);

Add Agent Tool

Attach a function tool to an agent. parameters is a full JSON Schema object sent to the LLM. endpoint_url is called by the voice pipeline when the LLM invokes the tool.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const tool = await client.addAgentTool("8a1b2c3d-4e5f-6789-abcd-ef0123456789", {
  name: "lookup_order",
  description: "Look up an order by ID",
  parameters: {
    type: "object",
    properties: { order_id: { type: "string" } },
    required: ["order_id"],
  },
  endpoint_url: "https://api.example.com/orders",
});
console.log(tool.id);

List Agent Tools

List all tools attached to an agent.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const tools = await client.listAgentTools("8a1b2c3d-4e5f-6789-abcd-ef0123456789");
for (const tool of tools) {
  console.log(tool.id, tool.name);
}

Update Agent Tool

Update a tool attached to an agent. Pass only the fields you want to change.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.updateAgentTool(
  "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  "3c2d1e0f-9a8b-7c6d-5e4f-3a2b1c0d9e8f",
  { description: "Look up an order by its internal ID" },
);

Delete Agent Tool

Remove a tool from an agent.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.deleteAgentTool(
  "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  "3c2d1e0f-9a8b-7c6d-5e4f-3a2b1c0d9e8f",
);

Upload Knowledge Document

Upload a document directly to the agent's knowledge base as multipart/form-data.

typescript
import { AethexAI } from "aethexai";
import { readFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const bytes = await readFile("faq.pdf");
const doc = await client.uploadKnowledgeDoc("8a1b2c3d-4e5f-6789-abcd-ef0123456789", {
  file: bytes,
  fileName: "faq.pdf",
  mimeType: "application/pdf",
  filename: "faq.pdf", // stored document name
});
console.log(doc.id);

Upload Knowledge Document by Upload

Upload a knowledge document via a presigned URL. Call presignUpload to get a signed PUT URL, PUT the file bytes with the supplied headers, then attach it with uploadKnowledgeDocByUpload.

typescript
import { AethexAI } from "aethexai";
import { readFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const upload = await client.presignUpload({
  kind: "doc/knowledge-base",
  filename: "faq.pdf",
  content_type: "application/pdf",
});
await fetch(upload.upload_url, {
  method: upload.method,
  body: await readFile("faq.pdf"),
  headers: upload.headers,
});

const doc = await client.uploadKnowledgeDocByUpload("8a1b2c3d-4e5f-6789-abcd-ef0123456789", {
  upload_id: upload.upload_id,
  filename: "faq.pdf",
});
console.log(doc.id);

List Knowledge Documents

List all documents in the agent's knowledge base.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const docs = await client.listKnowledgeDocs("8a1b2c3d-4e5f-6789-abcd-ef0123456789");
for (const doc of docs) {
  console.log(doc.id, doc.filename, doc.status);
}

Process Knowledge Document

Trigger embedding and indexing for a previously uploaded document. Ingestion is asynchronous, so queryKnowledgeBase may return empty results until processing completes.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.processKnowledgeDoc(
  "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  "b3c4d5e6-7f8a-9b0c-1d2e-3f4a5b6c7d8e",
);

Query Knowledge Base

Run a hybrid retrieval query against the agent's knowledge base. Returns RRF-ranked text chunks; score is the relevance value used for filtering, not the RRF score.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const results = await client.queryKnowledgeBase("8a1b2c3d-4e5f-6789-abcd-ef0123456789", {
  query: "What is the refund policy?",
});
for (const r of results.results) {
  console.log(r.score, r.text);
}

Get Knowledge Texts

Retrieve the extracted text for each document in the knowledge base.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const texts = await client.getKnowledgeTexts("8a1b2c3d-4e5f-6789-abcd-ef0123456789");
for (const doc of texts.documents) {
  console.log(doc.text.slice(0, 100), doc.filename);
}

Delete Knowledge Document

Remove a document from the agent's knowledge base.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.deleteKnowledgeDoc(
  "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  "b3c4d5e6-7f8a-9b0c-1d2e-3f4a5b6c7d8e",
);

Calls

Trigger outbound calls and query call history. triggerCall places an actual phone call using the agent's configured voice pipeline; the from_number must be registered to your tenant.

List Calls

List all call records for your tenant with optional pagination.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listCalls({ limit: 10, offset: 0 });
for (const call of page) {
  console.log(call.id, call.status, call.direction);
}

Create Call Record

Create a manual call record without triggering an actual call. Useful for logging external calls.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const call = await client.createCallRecord({
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  direction: "inbound",
  from_number: "+14155551234",
  to_number: "+18005550000",
  metadata: { source: "ivr-callback" },
});
console.log(call.id);

Trigger Call

Place an outbound phone call with the specified agent. The from_number must be registered to your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const call = await client.triggerCall({
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  to_number: "+14155551234",
  from_number: "+18005550000",
  metadata: { customer_id: "cu_42" },
});
console.log(call.id, call.status);

Batch Calls

Trigger multiple outbound calls in a single request. Each recipient can have individual variables for prompt interpolation.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const batch = await client.batchCalls({
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  recipients: [
    { to_number: "+14155551234", from_number: "+18005550000", variables: { customer_name: "Alice" } },
    { to_number: "+14155555678", from_number: "+18005550000", variables: { customer_name: "Bob" } },
  ],
  name: "Spring launch",
});
console.log(batch.batch_id, batch.status, batch.total);

Get Call Batch

Retrieve the status and progress of a batch call job.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const batch = await client.getCallBatch("5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b");
console.log(batch.status, batch.completed, batch.failed, batch.total);

Get Call

Retrieve a single call record by ID. Returns a typed CallResponse.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const call = await client.getCall("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
console.log(call.status, call.duration_seconds);

Get Call Status

Poll the live status of a call by ID. Returns a typed CallStatusResponse.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const status = await client.getCallStatus("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
console.log(status.status);

Conversation (WebRTC)

Low-level signalling for live WebRTC voice sessions. Most apps should use the Conversation helper (see Realtime conversations), which performs this connect → offer → ICE handshake for you. Use these methods directly only for a custom transport.

Connect

Create a new WebRTC session. Returns session_id and ICE configuration.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const session = await client.conversationConnect({ agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789" });
console.log(session.session_id, session.ice_config);

Send Offer

Submit the WebRTC offer SDP. Returns the server's answer SDP.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const answer = await client.sendOffer("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d", {
  sdp: "v=0\r\no=- ...\r\n",
  type: "offer",
});
console.log(answer.sdp, answer.pc_id); // pc_id identifies the peer connection for ICE

Send ICE Candidate

Relay ICE candidates to the server to complete peer connection setup.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

// pc_id is the peer-connection id returned by sendOffer, not the session id.
const answer = await client.sendOffer("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d", {
  sdp: "v=0\r\no=- ...\r\n",
  type: "offer",
});

await client.sendIceCandidate("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d", {
  pc_id: answer.pc_id,
  candidates: [
    { candidate: "candidate:842163049 1 udp 1677729535 ...", sdp_mid: "0", sdp_mline_index: 0 },
  ],
});

Get Session Status

Poll the status and metrics of an active WebRTC session.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const status = await client.getConversationSessionStatus("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d");
console.log(status.status, status.duration_s, status.turn_count);

End Session

Terminate an active WebRTC conversation session.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.endConversationSession("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d");

Send Tool Result

Return the result of a tool call to the agent during an active session.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.sendToolResult("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d", {
  tool_call_id: "call_abc123",
  result: JSON.stringify({ order_status: "shipped" }),
});

Conversations

Read completed conversation records: list, search, fetch transcripts, and download or stream audio.

List Conversations

List completed conversation records with optional pagination.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listConversations({ limit: 10, offset: 0 });
for (const conv of page) {
  console.log(conv.id, conv.status, conv.total_duration_ms);
}

Search Conversations

Full-text search across conversation transcripts.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const response = await client.searchConversations("refund policy", { limit: 10 });
for (const hit of response.results) {
  console.log(hit.conversation_id, hit.text);
}

Get Conversation

Retrieve a single conversation record. Returns a typed ConversationResponse.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const conv = await client.getConversation("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
console.log(conv.id, conv.status, conv.total_duration_ms);

Get Transcript

Retrieve the turn-by-turn transcript for a conversation. Returns an array of typed ConversationTurnResponse items.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const turns = await client.getTranscript("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
for (const turn of turns) {
  console.log(`${turn.role}: ${turn.text}`);
}

Get Audio URL

Get a short-lived signed URL to the conversation audio recording.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const audio = await client.getAudio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
console.log(audio.url, audio.format, audio.expires_in);

Get Audio Bytes

Download the conversation audio recording as raw bytes using the Kora client.

typescript
import { Kora } from "aethexai";
import { writeFile } from "node:fs/promises";

const kora = new Kora("https://api.aethexai.com", "ae_live_...");

const audio = await kora.getConversationAudio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
await writeFile("conversation.wav", audio); // Uint8Array

Revoke Audio Token

Revoke a previously issued audio playback token to prevent further access.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

// Mint a URL, then extract its token to revoke it.
const audio = await client.getAudio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f");
const token = new URL(audio.url).searchParams.get("token")!;

await client.revokeAudioToken("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f", { token });

Submit Feedback

Attach a quality rating and optional comment to a conversation.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.submitFeedback("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f", {
  rating: 5,
  comment: "Resolved my issue quickly.",
});

Transcription

Transcribe audio synchronously or as asynchronous jobs, inline or from a presigned upload.

Transcribe (Sync)

Transcribe audio synchronously using the Kora client. Blocks until the result is ready.

typescript
import { Kora } from "aethexai";
import { readFile } from "node:fs/promises";

const kora = new Kora("https://api.aethexai.com", "ae_live_...");

const bytes = await readFile("recording.wav");
const result = await kora.transcribe(bytes, { language: "english" });
console.log(result.text);

Transcribe (Async)

Submit audio for asynchronous transcription. Returns a job ID to poll.

typescript
import { Kora } from "aethexai";
import { readFile } from "node:fs/promises";

const kora = new Kora("https://api.aethexai.com", "ae_live_...");

const bytes = await readFile("long_recording.wav");
const job = await kora.transcribeAsync(bytes, {
  language: "english",
  webhookUrl: "https://example.com/webhooks/transcription",
});
console.log(job.id, job.status);

Transcribe by Upload

Transcribe audio that was previously uploaded via a presigned URL. Synchronous.

typescript
import { AethexAI } from "aethexai";
import { readFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const upload = await client.presignUpload({ kind: "audio/transcribe", filename: "recording.wav", content_type: "audio/wav" });
await fetch(upload.upload_url, { method: upload.method, body: await readFile("recording.wav"), headers: upload.headers });

const result = await client.transcribeAudioByUpload({ upload_id: upload.upload_id, language: "english" });
console.log(result.text);

Transcribe Async by Upload

Submit presigned-URL audio for asynchronous transcription with webhook delivery.

typescript
import { AethexAI } from "aethexai";
import { readFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const upload = await client.presignUpload({ kind: "audio/transcribe", filename: "long_recording.wav", content_type: "audio/wav" });
await fetch(upload.upload_url, { method: upload.method, body: await readFile("long_recording.wav"), headers: upload.headers });

const job = await client.transcribeAudioAsyncByUpload({
  upload_id: upload.upload_id,
  language: "english",
  webhook_url: "https://example.com/webhooks/transcription",
});
console.log(job.id, job.status);

Get Transcription Job

Poll the status of an async transcription job. Returns the result when status is completed.

typescript
import { Kora } from "aethexai";

const kora = new Kora("https://api.aethexai.com", "ae_live_...");

const job = await kora.getTranscribeJob("a1c4e7d2-5b9f-4e83-8c1a-7e0b9d4f2a16");
console.log(job.status, job.status === "completed" ? job.text : job.error_message);

Cancel Transcription Job

Cancel a pending or in-progress async transcription job.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.cancelTranscriptionJob("a1c4e7d2-5b9f-4e83-8c1a-7e0b9d4f2a16");

Text-to-Speech

Synthesize speech to audio bytes, stream it chunk by chunk, or submit batches.

Synthesize Speech

Convert up to 650 characters of text to a full WAV audio file. The header is included so the file is playable as-is.

typescript
import { AethexAI } from "aethexai";
import { writeFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });
const voices = await client.listVoices({ language: "english", limit: 1 });

const audio = await client.synthesizeSpeech({
  text: "Hello, welcome to AethexAI!",
  voice_id: voices.at(0)!.id,
  language: "english",
});
await writeFile("output.wav", audio); // Uint8Array

Stream Speech

Stream up to 650 characters of speech as raw 16-bit mono PCM chunks at 24 kHz for lower time-to-first-byte.

typescript
import { AethexAI } from "aethexai";
import { createWriteStream } from "node:fs";

const client = new AethexAI({ apiKey: "ae_live_..." });
const voices = await client.listVoices({ language: "english", limit: 1 });

const out = createWriteStream("output.pcm");
for await (const chunk of client.streamSpeech({
  text: "Streaming hello.",
  voice_id: voices.at(0)!.id,
  language: "english",
})) {
  out.write(chunk); // PCM16 @ 24kHz (Uint8Array)
}
out.end();

Batch Synthesize

Submit a list of texts for asynchronous speech synthesis with webhook delivery. Each item can be up to 3000 characters; the platform splits long items into safe backend chunks and returns one WAV per item.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const batch = await client.batchSynthesize({
  language: "english",
  items: [{ text: "First message." }, { text: "Second message." }],
  webhook_url: "https://example.com/webhooks/tts",
});
console.log(batch.batch_id, batch.status, batch.total);

Get TTS Batch

Retrieve the status and per-item results of a TTS batch job.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const batch = await client.getTtsBatch("8e7d6c5b-4a3b-2c1d-0e9f-8a7b6c5d4e3f");
console.log(batch.status, batch.completed, batch.failed, batch.total);
for (const item of batch.results) {
  console.log(item.index, item.status, item.audio_url);
}

Voices

Browse the voice catalog and generate short previews.

List Voices

List all voices available to your tenant with optional language filter and pagination.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const voices = await client.listVoices({ language: "english", limit: 20 });
for (const v of voices) {
  console.log(v.id, v.name, v.language);
}

Get Voice

Retrieve a single voice by ID or name.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });
const voices = await client.listVoices({ language: "english", limit: 1 });

const voice = await client.getVoice(voices.at(0)!.id);
console.log(voice.id, voice.name, voice.supports_dialect_style);

Preview Voice

Generate a short audio clip to preview a voice.

typescript
import { AethexAI } from "aethexai";
import { writeFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });
const voices = await client.listVoices({ language: "english", limit: 1 });

const preview = await client.previewVoice({
  voice_id: voices.at(0)!.id,
  text: "Welcome to Aethex AI.",
});
await writeFile("preview.wav", preview); // Uint8Array

Phone Numbers

Provision and configure phone numbers from Twilio or SIP, and set inbound routing.

List Phone Numbers

List all phone numbers registered to your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listPhoneNumbers({ limit: 20, offset: 0 });
for (const number of page) {
  console.log(number.id, number.phone_number, number.provider);
}

Register Twilio Number

Link a Twilio phone number to an agent for inbound and outbound calling.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const number = await client.registerTwilioPhoneNumber({
  twilio_account_id: "0a1b2c3d-4e5f-6789-abcd-ef0123456789",
  phone_number: "+18005550000",
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  friendly_name: "Sales line",
});
console.log(number.id);

Register SIP Number

Link a SIP trunk phone number to an agent.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const number = await client.registerSipPhoneNumber({
  phone_number: "+18005550000",
  trunk_id: "2a4b6c8d-1e3f-5a7b-9c0d-2e4f6a8b0c1d",
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  friendly_name: "Inbound support",
});
console.log(number.id);

Get Phone Number

Retrieve a single phone number record by ID.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const number = await client.getPhoneNumber("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d");
console.log(number);

Update Phone Number

Update the assigned agent or configuration for a registered phone number.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.updatePhoneNumber("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", {
  agent_id: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
});

Release Phone Number

Remove a registered phone number from your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.releasePhoneNumber("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d");

Set Phone Number Routing

Configure fallback routing for a phone number (e.g. forward on no-answer).

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.setPhoneNumberRouting("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", {
  fallback_action: "forward",
  fallback_number: "+18005550000",
  max_ring_seconds: 30,
});

Twilio Accounts

Connect and manage your own (Bring-Your-Own) Twilio accounts.

Register Twilio Account

Connect a Twilio account to your AethexAI tenant using an Account SID and Auth Token.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const account = await client.registerTwilioAccount({
  account_sid: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  auth_token: "your_twilio_auth_token",
  friendly_name: "Production Twilio",
});
console.log(account.id);

List Twilio Accounts

List all Twilio accounts registered to your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listTwilioAccounts({ limit: 20, offset: 0 });
for (const acct of page) {
  console.log(acct.id, acct.friendly_name);
}

Get Twilio Account

Retrieve a single Twilio account record by ID.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const account = await client.getTwilioAccount("0a1b2c3d-4e5f-6789-abcd-ef0123456789");
console.log(account);

Release Twilio Account

Disconnect a Twilio account from your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.releaseTwilioAccount("0a1b2c3d-4e5f-6789-abcd-ef0123456789");

Recordings

List, fetch, download, and delete call recordings.

List Recordings

List all recordings for your tenant with optional pagination.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const page = await client.listRecordings({ limit: 10, offset: 0 });
for (const rec of page) {
  console.log(rec.id, rec.duration_seconds);
}

Get Recording

Retrieve a single recording record by ID.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const rec = await client.getRecording("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b");
console.log(rec);

Get Recording Audio

Get a signed URL to the recording audio and download it.

typescript
import { AethexAI } from "aethexai";
import { writeFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const result = await client.getRecordingAudio("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b");
const audio = new Uint8Array(await (await fetch(result.url)).arrayBuffer());
await writeFile("recording.wav", audio);

Delete Recording

Permanently delete a recording.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.deleteRecording("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b");

Models

List the LLM and voice models available to your tenant.

List Models

List all models, with an optional includeUnavailable flag.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const models = await client.listModels({ includeUnavailable: false });
for (const model of models) {
  console.log(model.id, model.provider, model.available);
}

Uploads

Request presigned URLs for direct file uploads (knowledge base documents, audio for transcription).

Presign Upload

Request a presigned URL for uploading a file. PUT the bytes to upload_url with the supplied headers, then pass upload_id to the by-upload endpoint that consumes it.

typescript
import { AethexAI } from "aethexai";
import { readFile } from "node:fs/promises";

const client = new AethexAI({ apiKey: "ae_live_..." });

const upload = await client.presignUpload({
  kind: "doc/knowledge-base",
  filename: "faq.pdf",
  content_type: "application/pdf",
});

await fetch(upload.upload_url, {
  method: upload.method,
  body: await readFile("faq.pdf"),
  headers: upload.headers,
});
console.log(upload.upload_id);

Usage & Billing

Read usage and billing-period summaries, and manage usage triggers (webhook subscriptions) and their deliveries.

Get Usage

Retrieve raw usage counters for your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const usage = await client.getUsage();
console.log(usage);

Get Usage Summary

Retrieve aggregated usage summary for the current billing period.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const summary = await client.getUsageSummary();
console.log(summary);

Get Daily Usage

Retrieve day-by-day usage for the past N days.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

for (const day of await client.getDailyUsage({ days: 30 })) {
  console.log(day.date, day.count, day.total_tokens, day.voice_seconds);
}

Get Monthly Usage

Retrieve month-by-month usage history.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

for (const month of await client.getMonthlyUsage()) {
  console.log(month.month, month.count, month.total_tokens, month.voice_seconds);
}

Create Trigger

Configure a usage threshold trigger that fires a webhook when the threshold is crossed.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const trigger = await client.createTrigger({
  resource_type: "voice_call",
  threshold_value: "180000", // string preserves precision; server parses to Decimal
  threshold_type: "count",
  period: "monthly",
  event_callback_url: "https://example.com/webhooks/usage",
});
console.log(trigger.id);

List Triggers

List all usage threshold triggers for your tenant.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

for (const trigger of await client.listTriggers()) {
  console.log(trigger.id, trigger.resource_type, trigger.threshold_value, trigger.is_active);
}

Update Trigger

Update the threshold or configuration of an existing trigger.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.updateTrigger("4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a", {
  threshold_value: "250000",
});

Rotate Webhook Secret

Rotate the shared webhook secret for trigger delivery. The SDK call rotates the secret but does not return its value; if you need to capture the new secret, send the raw HTTP request instead, which returns it in the response. Each call rotates and revokes the previous secret, so use one approach or the other.

List Trigger Firings

Retrieve the delivery history for a trigger.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

const firings = await client.listTriggerFirings("4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a", { limit: 50 });
for (const firing of firings) {
  console.log(firing.id, firing.fired_at, firing.delivery_status, firing.attempt_count);
}

Redeliver Firing

Retry delivery of a previously failed trigger firing.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

await client.redeliverFiring(
  "4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
  "7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
);

Pagination

List methods return a single-page PaginatedResponse. It is iterable, has a .length, supports .at(i), and exposes .hasMore — loop while .hasMore to consume every page.

typescript
import { AethexAI } from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

let offset = 0;
const limit = 50;
for (;;) {
  const page = await client.listCalls({ offset, limit });
  for (const call of page) console.log(call.id);
  if (!page.hasMore) break;
  offset += limit;
}

Realtime conversations

Full-duplex WebRTC conversations with the same voice pipeline your end users get from the dashboard. The browser-first Conversation helper (aethexai/realtime) performs the entire connect → offer → ICE handshake, captures the microphone, plays the agent audio, and surfaces transcripts and tool calls as typed events.

Heads up
Never ship a tenant API key (ae_live...) to an untrusted browser. Anyone can read it from your page and call the API as you. For public web apps, proxy the connect and offer signalling through your own backend (as the WebRTC quickstart shows) so the key stays server-side; the inline key below is only for first-party or server-side use. The Conversation helper runs the WebRTC signalling for you, so you never call conversationConnect, sendOffer, or sendIceCandidate yourself. In a browser it uses native WebRTC; in Node.js, pass the @roamhq/wrtc adapter (see below). The microphone requires a secure context (https or localhost) and a user gesture.
typescript
import { AethexAI } from "aethexai";
import { Conversation } from "aethexai/realtime";

// In an untrusted browser, never embed ae_live directly: configure the client
// with a custom fetch that proxies signalling through your backend (see Quickstart).
const client = new AethexAI({ apiKey: "ae_live_..." });
const convo = new Conversation(client, { agentId: "8a1b2c3d-4e5f-6789-abcd-ef0123456789" });

convo.on("agentText", (text) => console.log("agent:", text));
convo.on("userTranscript", (text) => console.log("user:", text));
convo.on("statusChange", (status) => console.log(status)); // connecting → connected → closed
convo.on("toolCall", async (call) => {
  await convo.sendToolResult(call.id, JSON.stringify({ ok: true }));
});

await convo.start(); // prompts for mic, runs the WebRTC handshake, resolves when connected
// ... the agent's audio plays automatically ...
await convo.end();

Realtime in Node.js

Node has no native WebRTC, so install the optional @roamhq/wrtc peer dependency and wire it in with createNodeWebRTC. By default it attaches a silent microphone (so a call connects for a connectivity test); push real PCM with node.pushPcm and receive the agent's audio frames with createAudioSink.

typescript
import { AethexAI } from "aethexai";
import { Conversation } from "aethexai/realtime";
import { createNodeWebRTC, createAudioSink } from "aethexai/realtime/node";

const client = new AethexAI({ apiKey: "ae_live_..." });
const node = await createNodeWebRTC(); // synthetic mic (silence by default)
const convo = new Conversation(client, {
  agentId: "8a1b2c3d-4e5f-6789-abcd-ef0123456789",
  ...node.conversationOptions,
});

convo.on("agentText", (t) => console.log("agent:", t));
convo.on("track", async (track) => {
  await createAudioSink(track, (frame) => {
    /* frame.samples is Int16 PCM at frame.sampleRate */
  });
});

await convo.start();
// node.pushPcm(int16Frame) to stream real mic audio (10ms frames at the sample rate)
await convo.end();
node.close();

TypeScript types

The generated request and response model types are exported under the Aethex namespace for advanced typing.

typescript
import type { Aethex } from "aethexai";

function summarize(agent: Aethex.AgentResponse): string {
  return `${agent.name} (${agent.id})`;
}

Error Handling

Non-2xx responses reject with typed errors; transport and timeout failures map to APIConnectionError / APITimeoutError. All errors inherit from AethexError, and status-derived errors extend APIStatusError carrying message, code, statusCode, response, and headers. RateLimitError exposes retryAfter.

typescript
import {
  AethexAI,
  AethexError,
  APIStatusError,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  ValidationError,
} from "aethexai";

const client = new AethexAI({ apiKey: "ae_live_..." });

try {
  const agent = await client.getAgent("00000000-0000-0000-0000-000000000000");
} catch (err) {
  if (err instanceof NotFoundError) console.log("Agent not found");
  else if (err instanceof AuthenticationError) console.log("Invalid API key");
  else if (err instanceof RateLimitError) console.log(`Rate limited. Retry after ${err.retryAfter}s`);
  else if (err instanceof ValidationError) console.log("Invalid request:", err.message);
  else if (err instanceof APIStatusError) console.log(`API error ${err.statusCode}: ${err.message}`);
  else if (err instanceof AethexError) console.log("SDK error:", err.message);
  else throw err;
}