Python SDK
Official Python SDK for the AethexAI Voice API. Create and manage production voice agents, trigger real phone calls, synthesize speech, transcribe audio, and run realtime conversations from Python.
Installation
The SDK ships as the aethexai package on PyPI and requires Python 3.10+. Install it with pip or uv using the commands below. The realtime extra additionally pulls aiortc and PyAV; PyAV ships prebuilt wheels for common platforms, but unusual platforms may need FFmpeg development headers before PyAV can build from source.
pip 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 is only for dashboard auth. Pass api_key explicitly or set AETHEX_API_KEY.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
# Verify the connection by listing available voices.
voices = client.list_voices(limit=5)
for voice in voices:
print(voice.id, voice.name)Context Manager
AethexAI and AsyncAethexAI support context managers so HTTP connections are closed when the block exits.
from aethexai import AethexAI
with AethexAI(api_key="ae_live_...") as client:
page = client.list_agents(limit=20)
print(page.data)Kora
Kora is the voice-focused client: agents, outbound calls, voices, TTS, transcription, and conversation history without the account-management surface. trigger_call requires a from_number that is registered to your tenant for outbound calling — the gateway rejects an omitted from_number with HTTP 422 (unless the server sets TWILIO_PHONE_NUMBER as a fallback).
from aethexai import Kora
kora = Kora(base_url="https://api.aethexai.com", api_key="ae_live_...")
voices = kora.list_voices(limit=1)
agent = kora.create_agent(
name="Support Agent",
system_prompt="You are a helpful customer support agent.",
first_message="Hi there! How can I help you today?",
voice_id=voices[0].id,
language="english",
)
with open("recording.wav", "rb") as f:
result = kora.transcribe(f.read(), language="english")
print(result.text)Agents
Create, list, retrieve, update, duplicate, and delete voice agents. All create/update methods accept keyword arguments that map to the API request body. Paginated endpoints return objects with .data, .total, .offset, and .limit; compute end-of-results as page.offset + len(page.data) >= page.total. The list_/create_/update_/duplicate_/trigger_call write methods currently surface the raw JSON dict, so use bracket access (e.g. agent["id"]); single-resource reads such as get_agent and the items in list_voices return typed attrs models, so attribute access works (agent.id, voice.id).
List Agents
List all agents in your tenant. Returns a paginated result with .data, .total, .offset, and .limit.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_agents(limit=20, offset=0)
for agent in page.data:
print(agent.id, agent.name)Create Agent
Create a new voice agent. Write methods return the underlying JSON dict — access fields with ["id"].
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
voices = client.list_voices(language="english", limit=1)
agent = client.create_agent(
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[0].id,
language="english",
)
print(agent.id)Get Agent
Retrieve a single agent by ID. Returns a typed AgentResponse — use attribute access (agent.id, agent.name).
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
agent = client.get_agent("8a1b2c3d-4e5f-6789-abcd-ef0123456789")
print(agent.id, agent.name, agent.voice_id)Update Agent
Update an existing agent. Pass only the fields you want to change.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.update_agent(
"8a1b2c3d-4e5f-6789-abcd-ef0123456789",
name="Updated Support Bot",
first_message="Welcome back!",
)Delete Agent
Permanently delete an agent by ID.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.delete_agent("8a1b2c3d-4e5f-6789-abcd-ef0123456789")Duplicate Agent
Create an exact copy of an existing agent. Returns the new agent dict.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
clone = client.duplicate_agent("8a1b2c3d-4e5f-6789-abcd-ef0123456789")
print(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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
tool = client.add_agent_tool(
"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",
)
print(tool["id"])List Agent Tools
List all tools attached to an agent.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
tools = client.list_agent_tools("8a1b2c3d-4e5f-6789-abcd-ef0123456789")
for tool in tools:
print(tool["id"], tool["name"])Update Agent Tool
Update a tool attached to an agent. Pass only the fields you want to change.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.update_agent_tool(
"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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.delete_agent_tool(
"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.
from aethexai import AethexAI
from aethexai._generated.types import File
from aethexai._generated.models import (
BodyUploadKnowledgeDocApiV1AgentsAgentIdKnowledgeBasePost as UploadBody,
)
client = AethexAI(api_key="ae_live_...")
with open("faq.pdf", "rb") as f:
body = UploadBody(
file=File(payload=f, file_name="faq.pdf", mime_type="application/pdf"),
filename="faq.pdf",
)
doc = client.upload_knowledge_doc(
"8a1b2c3d-4e5f-6789-abcd-ef0123456789",
body=body,
)
print(doc["id"])Upload Knowledge Document by Upload
Upload a knowledge document via a presigned URL. Call presign_upload to get a signed PUT URL, PUT the file bytes, then register the upload with this method.
from aethexai import AethexAI
import httpx
client = AethexAI(api_key="ae_live_...")
upload = client.presign_upload(kind="doc/knowledge-base", filename="faq.pdf", content_type="application/pdf")
with open("faq.pdf", "rb") as f:
httpx.request(upload.method, upload.upload_url, content=f.read(),
headers=dict(upload.headers.additional_properties)).raise_for_status()
doc = client.upload_knowledge_doc_by_upload(
"8a1b2c3d-4e5f-6789-abcd-ef0123456789",
upload_id=upload.upload_id,
filename="faq.pdf",
)
print(doc["id"])List Knowledge Documents
List all documents in the agent's knowledge base.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
docs = client.list_knowledge_docs("8a1b2c3d-4e5f-6789-abcd-ef0123456789")
for doc in docs:
print(doc["id"], doc["filename"], doc["status"])Process Knowledge Document
Trigger embedding and indexing for a previously uploaded document. Ingestion is asynchronous — query_knowledge_base may return empty results until processing completes.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.process_knowledge_doc(
"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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
results = client.query_knowledge_base(
"8a1b2c3d-4e5f-6789-abcd-ef0123456789",
query="What is the refund policy?",
)
for r in results.results:
print(r.score, r.text)Get Knowledge Texts
Retrieve the extracted text for each document in the knowledge base.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
texts = client.get_knowledge_texts("8a1b2c3d-4e5f-6789-abcd-ef0123456789")
for t in texts["documents"]:
print(t['text'][:100], t['filename'])Delete Knowledge Document
Remove a document from the agent's knowledge base.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.delete_knowledge_doc(
"8a1b2c3d-4e5f-6789-abcd-ef0123456789",
"b3c4d5e6-7f8a-9b0c-1d2e-3f4a5b6c7d8e",
)Calls
Trigger outbound calls and query call history. trigger_call places an actual phone call; the agent system_prompt and first_message drive the conversation through the STT, LLM, and TTS pipeline. The from_number must be registered to your tenant for outbound calling — the gateway rejects an omitted from_number with HTTP 422 (unless the server sets TWILIO_PHONE_NUMBER as a fallback). trigger_call and list_calls items return dicts; get_call_status returns a typed CallStatusResponse.
List Calls
List all call records for your tenant with optional pagination.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_calls(limit=10, offset=0)
for call in page.data:
print(call.id, call.status, call.direction)Create Call Record
Create a manual call record without triggering an actual call. Useful for logging external calls.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
call = client.create_call_record(
agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789",
direction="inbound",
from_number="+14155551234",
to_number="+18005550000",
metadata={"source": "ivr-callback"},
)
print(call["id"])Trigger Call
Place an outbound phone call with the specified agent. The from_number must be registered to your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
call = client.trigger_call(
agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789",
to_number="+14155551234",
from_number="+18005550000",
metadata={"customer_id": "cu_42"},
)
print(call["id"], call["status"])Batch Calls
Trigger multiple outbound calls in a single request. Each recipient can have individual variables for prompt interpolation.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
batch = client.batch_calls(
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",
)
print(batch.batch_id, batch.status, batch.total)Get Call Batch
Retrieve the status and progress of a batch call job.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
batch = client.get_call_batch("5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b")
print(batch.status, batch.completed, batch.failed, batch.total)Get Call
Retrieve a single call record by ID. Returns a typed CallResponse.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
call = client.get_call("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
print(call.status, call.duration_seconds)Get Call Status
Poll the live status of a call by ID. Returns a typed CallStatusResponse.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
status = client.get_call_status("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
print(status.status)Conversation (WebRTC)
Low-level WebRTC signalling for browser-to-agent voice conversations. Use the Realtime SDK for a higher-level interface.
Connect
Create a new WebRTC session. Returns session_id and ICE configuration.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
session = client.conversation_connect(agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789")
print(session["session_id"], session["ice_config"])Send Offer
Submit the WebRTC offer SDP. Returns the server's answer SDP.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
answer = client.send_offer(
"3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d",
sdp="v=0\r\no=- ...\r\n",
type="offer",
)
print(answer["sdp"])Send ICE Candidate
Relay ICE candidates to the server to complete peer connection setup.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.send_ice_candidate(
"3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d",
pc_id="3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d",
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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
status = client.get_conversation_session_status("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d")
print(status["status"], status["duration_s"], status["turn_count"])End Session
Terminate an active WebRTC conversation session.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.end_conversation_session("3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d")Send Tool Result
Return the result of a tool call to the agent during an active session.
import json
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.send_tool_result(
"3f2a7b1c-8d4e-4a5b-9c2d-1e0f3a4b5c6d",
tool_call_id="call_abc123",
result=json.dumps({"order_status": "shipped"}),
)Conversations
Retrieve conversation records, transcripts, and recordings from completed calls. get_conversation returns a typed ConversationResponse and get_transcript returns a list of typed ConversationTurnResponse items, so attribute access works on both. The downloadable recording lives on Kora as get_conversation_audio (returns raw bytes); the equivalent AethexAI methods return signed URLs or stream metadata instead, so use Kora when you want the file bytes directly.
List Conversations
List completed conversation records with optional pagination.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_conversations(limit=10, offset=0)
for conv in page.data:
print(conv.id, conv.status, conv.total_duration_ms)Search Conversations
Full-text search across conversation transcripts.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
response = client.search_conversations("refund policy", limit=10)
for hit in response["results"]:
print(hit["conversation_id"], hit["text"])Get Conversation
Retrieve a single conversation record. Returns a typed ConversationResponse.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
conv = client.get_conversation("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
print(conv.id, conv.status, conv.total_duration_ms)Get Transcript
Retrieve the turn-by-turn transcript for a conversation. Returns a list of typed ConversationTurnResponse items.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
turns = client.get_transcript("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
for turn in turns:
print(f"{turn.role}: {turn.text}")Get Audio URL
Get a short-lived signed URL to the conversation audio recording.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
audio = client.get_audio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
print(audio["url"], audio["format"], audio["expires_in"])Get Audio Bytes
Download the conversation audio recording as raw bytes using the Kora client.
from aethexai import Kora
kora = Kora(base_url="https://api.aethexai.com", api_key="ae_live_...")
audio = kora.get_conversation_audio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
with open("conversation.wav", "wb") as f:
f.write(audio)Revoke Audio Token
Revoke a previously issued audio playback token to prevent further access.
from urllib.parse import urlparse, parse_qs
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
# Mint a URL, then extract its token to revoke it.
audio = client.get_audio("c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f")
token = parse_qs(urlparse(audio["url"]).query)["token"][0]
client.revoke_audio_token(
"c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
token=token,
)Submit Feedback
Attach a quality rating and optional comment to a conversation.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.submit_feedback(
"c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
rating=5,
comment="Resolved my issue quickly.",
)Transcription
Transcribe audio files synchronously or submit them for asynchronous processing. The sync method blocks until the result is ready. The async variant returns a job whose status you can poll.
Transcribe (Sync)
Transcribe audio synchronously using the Kora client. Blocks until the result is ready.
from aethexai import Kora
kora = Kora(base_url="https://api.aethexai.com", api_key="ae_live_...")
with open("recording.wav", "rb") as f:
result = kora.transcribe(f.read(), language="english")
print(result.text)Transcribe (Async)
Submit audio for asynchronous transcription. Returns a job ID to poll.
from aethexai import Kora
kora = Kora(base_url="https://api.aethexai.com", api_key="ae_live_...")
with open("long_recording.wav", "rb") as f:
job = kora.transcribe_async(
f.read(),
language="english",
webhook_url="https://example.com/webhooks/transcription",
)
print(job.id, job.status)Transcribe by Upload
Transcribe audio that was previously uploaded via a presigned URL. Synchronous.
from aethexai import AethexAI
import httpx
client = AethexAI(api_key="ae_live_...")
upload = client.presign_upload(kind="audio/transcribe", filename="recording.wav", content_type="audio/wav")
with open("recording.wav", "rb") as f:
httpx.request(upload.method, upload.upload_url, content=f.read(),
headers=dict(upload.headers.additional_properties)).raise_for_status()
result = client.transcribe_audio_by_upload(upload_id=upload.upload_id, language="english")
print(result.text)Transcribe Async by Upload
Submit presigned-URL audio for asynchronous transcription with webhook delivery.
from aethexai import AethexAI
import httpx
client = AethexAI(api_key="ae_live_...")
upload = client.presign_upload(kind="audio/transcribe", filename="long.wav", content_type="audio/wav")
with open("long_recording.wav", "rb") as f:
httpx.request(upload.method, upload.upload_url, content=f.read(),
headers=dict(upload.headers.additional_properties)).raise_for_status()
job = client.transcribe_audio_async_by_upload(
upload_id=upload.upload_id,
language="english",
webhook_url="https://example.com/webhooks/transcription",
)
print(job.id, job.status)Get Transcription Job
Poll the status of an async transcription job. Returns the result when status is completed.
from aethexai import Kora
kora = Kora(base_url="https://api.aethexai.com", api_key="ae_live_...")
job = kora.get_transcribe_job("a1c4e7d2-5b9f-4e83-8c1a-7e0b9d4f2a16")
print(job.status, job.text if job.status == "completed" else job.error_message)Cancel Transcription Job
Cancel a pending or in-progress async transcription job.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.cancel_transcription_job("a1c4e7d2-5b9f-4e83-8c1a-7e0b9d4f2a16")Text-to-Speech
Convert text to speech. synthesize_speech returns the full audio as a complete WAV (header included, so the file is playable as-is). stream_speech yields raw 16-bit mono PCM frames at 24 kHz for lower time-to-first-byte playback — wrap them in a WAV header yourself, or pipe straight into a player that consumes PCM (for example, ffplay -f s16le -ar 24000). Keep realtime synthesize/stream inputs to 650 characters or fewer; use batch_synthesize for long-form scripts up to 3000 characters per item.
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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
audio = client.synthesize_speech(
text="Hello, welcome to AethexAI!",
language="english",
)
with open("output.wav", "wb") as f:
f.write(audio)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.
import wave
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
with wave.open("output.wav", "wb") as wav:
wav.setnchannels(1)
wav.setsampwidth(2)
wav.setframerate(24_000)
for chunk in client.stream_speech(
text="Streaming hello.",
language="english",
):
wav.writeframes(chunk)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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
batch = client.batch_synthesize(
language="english",
items=[
{"text": "First message."},
{"text": "Second message."},
],
webhook_url="https://example.com/webhooks/tts",
)
print(batch.batch_id, batch.status, batch.total)Get TTS Batch
Retrieve the status and per-item results of a TTS batch job.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
batch = client.get_tts_batch("8e7d6c5b-4a3b-2c1d-0e9f-8a7b6c5d4e3f")
print(batch.status, batch.completed, batch.failed, batch.total)
for item in batch.results:
print(item.index, item.status, item.audio_url)Voices
List and retrieve available voices. Returns typed VoiceResponse objects so attribute access works.
List Voices
List all voices available to your tenant with optional language filter and pagination.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
voices = client.list_voices(language="english", limit=20)
for v in voices:
print(v.id, v.name, v.language)Get Voice
Retrieve a single voice by ID or name.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
voices = client.list_voices(language="english", limit=1)
voice = client.get_voice(voices[0].id)
print(voice.id, voice.name, voice.supports_dialect_style)Preview Voice
Generate a short audio clip to preview a voice. Workaround: use synthesize_speech until the preview_voice UTF-8 bug is fixed.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
voices = client.list_voices(language="english", limit=1)
# Workaround: use synthesize_speech until preview_voice's UTF-8 bug is fixed.
preview = client.synthesize_speech(
voice_id=voices[0].id,
text="Welcome to Aethex AI.",
language="english",
)
with open("preview.wav", "wb") as f:
f.write(preview)Phone Numbers
Register, list, and manage phone numbers for inbound and outbound calling.
List Phone Numbers
List all phone numbers registered to your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_phone_numbers(limit=20, offset=0)
for number in page.data:
print(number["id"], number["phone_number"], number["provider"])Register Twilio Number
Link a Twilio phone number to an agent for inbound and outbound calling.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
number = client.register_twilio_phone_number(
twilio_account_id="0a1b2c3d-4e5f-6789-abcd-ef0123456789",
phone_number="+18005550000",
agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789",
friendly_name="Sales line",
)
print(number["id"])Register SIP Number
Link a SIP trunk phone number to an agent.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
number = client.register_sip_phone_number(
phone_number="+18005550000",
trunk_id="2a4b6c8d-1e3f-5a7b-9c0d-2e4f6a8b0c1d",
agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789",
friendly_name="Inbound support",
)
print(number["id"])Get Phone Number
Retrieve a single phone number record by ID.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
number = client.get_phone_number("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d")
print(number)Update Phone Number
Update the assigned agent or configuration for a registered phone number.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.update_phone_number(
"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
agent_id="8a1b2c3d-4e5f-6789-abcd-ef0123456789",
)Release Phone Number
Remove a registered phone number from your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.release_phone_number("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d")Set Phone Number Routing
Configure fallback routing for a phone number (e.g. forward on no-answer).
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.set_phone_number_routing(
"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
fallback_action="forward",
fallback_number="+18005550000",
max_ring_seconds=30,
)Twilio Accounts
Register and manage Twilio credentials used for outbound calling.
Register Twilio Account
Connect a Twilio account to your AethexAI tenant using an Account SID and Auth Token.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
account = client.register_twilio_account(
account_sid="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
auth_token="your_twilio_auth_token",
friendly_name="Production Twilio",
)
print(account.id)List Twilio Accounts
List all Twilio accounts registered to your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_twilio_accounts(limit=20, offset=0)
for acct in page.data:
print(acct["id"], acct["friendly_name"])Get Twilio Account
Retrieve a single Twilio account record by ID.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
account = client.get_twilio_account("0a1b2c3d-4e5f-6789-abcd-ef0123456789")
print(account)Release Twilio Account
Disconnect a Twilio account from your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.release_twilio_account("0a1b2c3d-4e5f-6789-abcd-ef0123456789")Recordings
List and download call recordings.
List Recordings
List all recordings for your tenant with optional pagination.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
page = client.list_recordings(limit=10, offset=0)
for rec in page.data:
print(rec["id"], rec["duration_seconds"])Get Recording
Retrieve a single recording record by ID.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
rec = client.get_recording("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b")
print(rec)Get Recording Audio
Get a signed URL to the recording audio and download it.
import httpx
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
result = client.get_recording_audio("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b")
audio = httpx.get(result["url"]).content
with open("recording.wav", "wb") as f:
f.write(audio)Delete Recording
Permanently delete a recording.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.delete_recording("5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b")Models
Query the available LLM models for your deployment.
List Models
List all models with optional include_unavailable flag.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
models = client.list_models(include_unavailable=False)
for model in models:
print(model.id, model.provider, model.available)Uploads
Get presigned URLs for direct file uploads to object storage.
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 registration endpoint.
from aethexai import AethexAI
import httpx
client = AethexAI(api_key="ae_live_...")
upload = client.presign_upload(
kind="doc/knowledge-base",
filename="faq.pdf",
content_type="application/pdf",
)
with open("faq.pdf", "rb") as f:
httpx.request(
upload.method,
upload.upload_url,
content=f.read(),
headers=dict(upload.headers.additional_properties),
).raise_for_status()
print(upload.upload_id)Usage & Billing
Query usage metrics, configure billing triggers, and manage webhook secrets.
Get Usage
Retrieve raw usage counters for your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
usage = client.get_usage()
print(usage)Get Usage Summary
Retrieve aggregated usage summary for the current billing period.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
summary = client.get_usage_summary()
print(summary)Get Daily Usage
Retrieve day-by-day usage for the past N days.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
for day in client.get_daily_usage(days=30):
print(day.date, day.count, day.total_tokens, day.voice_seconds)Get Monthly Usage
Retrieve month-by-month usage history.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
for month in client.get_monthly_usage():
print(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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
trigger = client.create_trigger(
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",
)
print(trigger.id)List Triggers
List all usage threshold triggers for your tenant.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
for trigger in client.list_triggers():
print(trigger.id, trigger.resource_type, trigger.threshold_value, trigger.is_active)Update Trigger
Update the threshold or configuration of an existing trigger.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.update_trigger(
"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.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
firings = client.list_trigger_firings("4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a", limit=50)
for firing in firings:
print(firing.id, firing.fired_at, firing.delivery_status, firing.attempt_count)Redeliver Firing
Retry delivery of a previously failed trigger firing.
from aethexai import AethexAI
client = AethexAI(api_key="ae_live_...")
client.redeliver_firing(
"4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
"7c6d5e4f-3a2b-1c0d-9e8f-7a6b5c4d3e2f",
)Async Support
AsyncAethexAI mirrors the sync client method-for-method. Use it with asyncio, FastAPI, workers, or any async framework where you want non-blocking HTTP calls.
import asyncio
from aethexai import AsyncAethexAI
async def main():
async with AsyncAethexAI(api_key="ae_live_...") as client:
voices = await client.list_voices(language="english")
agent = await client.create_agent(
name="Async Bot",
system_prompt="You are a helpful customer support agent.",
voice_id=voices[0].id,
)
call = await client.trigger_call(
agent_id=agent.id,
to_number="+14155551234",
from_number="+18005550000",
)
print(call["id"], call["status"])
asyncio.run(main())Realtime Conversations
Use the realtime extra for full-duplex WebRTC conversations with the same voice pipeline your end users get from the dashboard. Requires the realtime extra · install with pip install "aethexai[realtime]". The realtime extra pulls aiortc and PyAV; PyAV needs the FFmpeg development headers on the host before the wheel can build (apt install libavformat-dev libavfilter-dev libavdevice-dev on Debian/Ubuntu, brew install ffmpeg on macOS).
Conversation attaches a silent input track, so the agent will only hear silence until you wire a real audio source. Pass any aiortc MediaStreamTrack (for example one backed by sounddevice) via the audio_input= keyword to send mic input. The agent_id below must be the UUID of an agent you have already created · derive it from list_agents() rather than hardcoding a placeholder.import asyncio
from aethexai import AsyncAethexAI
from aethexai.realtime import Conversation, ConversationCallbacks
async def main():
async with AsyncAethexAI(api_key="ae_live_...") as client:
page = await client.list_agents(limit=1)
if not page.data:
raise SystemExit("Create an agent before starting a realtime call")
agent_id = page.data[0].id
conversation = Conversation(
client,
agent_id=agent_id,
callbacks=ConversationCallbacks(
on_agent_text=lambda text: print("agent:", text),
on_user_transcript=lambda text: print("user:", text),
),
# audio_input=<your aiortc MediaStreamTrack from sounddevice>,
)
await conversation.start()
await asyncio.sleep(30)
await conversation.end()
asyncio.run(main())Error Handling
The SDK raises typed exceptions for API and transport errors. All exceptions inherit from AethexError. APIStatusError subclasses carry the HTTP status code, error code, headers, and raw response body. RateLimitError includes a retry_after property.
from aethexai import (
AethexAI,
AethexError,
APIStatusError,
AuthenticationError,
NotFoundError,
RateLimitError,
ValidationError,
)
client = AethexAI(api_key="ae_live_...")
try:
agent = client.get_agent("00000000-0000-0000-0000-000000000000")
except NotFoundError:
print("Agent not found")
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except ValidationError as e:
print(f"Invalid request: {e.message}")
except APIStatusError as e:
print(f"API error {e.status_code}: {e.message}")
except AethexError as e:
print(f"SDK error: {e}")