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.
npm install aethexaiQuick 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.
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.
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.
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.
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.
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'].
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 ICESend ICE Candidate
Relay ICE candidates to the server to complete peer connection setup.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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); // Uint8ArrayRevoke Audio Token
Revoke a previously issued audio playback token to prevent further access.
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.
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.
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.
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.
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.
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.
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.
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.
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); // Uint8ArrayStream Speech
Stream up to 650 characters of speech as raw 16-bit mono PCM chunks at 24 kHz for lower time-to-first-byte.
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.
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.
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.
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.
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.
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); // Uint8ArrayPhone 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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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;
}