Telenow voice AI SDK for Python — place AI agent phone calls, mint browser/app call sessions, transfer & end live calls, verify webhooks, and stream your own LLM into calls (FastAPI/Django SSE helpers). Stdlib only.
Project description
telenow (Python)
Telenow voice AI SDK for Python 3.8+ — stdlib only
(no requests), fully typed (py.typed). From your backend you can: place AI
agent phone calls, mint browser/app call sessions, transfer/end
live calls, verify webhooks, manage agents, and build Custom API
(bring-your-own-LLM) streaming endpoints for FastAPI, Django, or Flask.
pip install telenow # core (zero dependencies)
pip install "telenow[django]" # + the Django webhook decorator
Before you start
- A Telenow account + agent (dashboard → Agents). Copy the agent ID from the agent page (or its Publish tab samples).
- An org API key — dashboard → Developers tab. Keep it server-side
only; it's sent as the
X-API-Keyheader. - Phone calls additionally need a phone number attached to the agent (dashboard → Numbers).
import os
from telenow import Telenow
tn = Telenow(
api_key=os.environ["TELENOW_API_KEY"], # required
base_url="https://api.telenow.ai", # default — override for self-hosted
)
Phone calls
result = tn.create_call(
"AGENT_UUID",
"+15551234567", # E.164
variables={"order_id": "A-1042"}, # context variables (required ones must be present)
identifier="customer-9", # trusted caller identity for the agent's tools
first_response="Hi! Calling about your delivery.",# optional opener override for THIS call
machine_detection="true", # "true" = auto-voicemail, "hangup" (Plivo)
)
session_id = result["sessionId"]
Control a live call (phone or web — the client SDKs expose
call.sessionId for exactly this):
tn.transfer_call(session_id, "+15557654321") # warm transfer to a human
tn.end_call(session_id) # hang up
Web calls (mint a session for your frontend)
The recommended browser/app flow: mint here, hand the result to the client, the client SDK connects — the API key never reaches the browser.
@app.post("/voice/session") # FastAPI shown; any framework works
async def voice_session():
return tn.init_web_call(
"AGENT_UUID",
variables={"customer_name": "Asha"}, # baked in server-side — client can't tamper
identifier="customer-9",
)
# → {"sessionId": ..., "websocketUrl": ...} → TelenowCall({ session })
Webhooks
Telenow signs every delivery with X-VoiceAI-Signature: sha256=<hex>
(HMAC-SHA256 of the raw body). Always verify before trusting the payload.
Django (decorator verifies + parses for you):
from telenow.django import telenow_webhook
@telenow_webhook(secret=settings.TELENOW_WEBHOOK_SECRET)
def hook(request, event): # signature verified, body parsed
if event["event"] == "call.ended": # also: call.started, transcript.ready, tool.invoked
...
return HttpResponse(status=200)
Anything else (FastAPI shown) — verify against the raw bytes:
from telenow import verify_webhook
@app.post("/webhooks/telenow")
async def hook(request: Request):
raw = await request.body()
if not verify_webhook(raw, request.headers.get("x-voiceai-signature", ""), SECRET):
raise HTTPException(401)
event = json.loads(raw)
...
Configure endpoints + events in the dashboard (Webhooks), per agent or org-wide. Payload shapes: webhook events reference.
Custom API — stream your own LLM into calls
When an agent's Brain is set to Custom API, Telenow runs STT + TTS and
POSTs each user turn to your endpoint (?calling=true&stream=true, JSON
body {"query": ..., "userId": ..., **payload}), then speaks your
Server-Sent-Events reply as tokens arrive. telenow.custom_api emits the
exact wire format.
FastAPI:
from telenow import custom_api
from fastapi.responses import StreamingResponse
@app.post("/telenow-llm")
async def llm(request: Request):
body = await request.json()
async def gen():
async for token in my_llm_stream(body["query"]):
yield token # str → spoken as it streams
# yield custom_api.call_end("Goodbye!") # dict → control event (hangs up after reply)
return StreamingResponse(
custom_api.sse_astream(gen()),
media_type=custom_api.MEDIA_TYPE,
headers=custom_api.SSE_HEADERS, # disables proxy buffering (X-Accel-Buffering: no)
)
Django / Flask (sync generators) — wrap with custom_api.sse_stream(gen()) in
a StreamingHttpResponse / Response with the same media type + headers.
Notes that save debugging time:
- Yield
strfor spoken token deltas; yielddict(e.g.custom_api.call_end()) for control events.[DONE]is appended for you — even if your generator raises midway, so the turn ends cleanly. - Without
SSE_HEADERS, nginx buffers your stream and the agent goes silent, then speaks everything at once. - Transfer is a REST action, not an SSE event: call
tn.transfer_call(session_id, to)from inside your handler.
Agents & everything else
tn.list_agents() / get_agent(id) / create_agent(data) / update_agent(id, data)
mirror the Agents API. Anything not
wrapped: call the REST API with the
same X-API-Key header.
Errors
Every non-2xx (and {"success": false} envelope) raises TelenowError
with .status and .body:
from telenow import TelenowError
try:
tn.create_call("AGENT_UUID", "+15551234567")
except TelenowError as e:
print(e.status, e, e.body)
| Status | Usual cause |
|---|---|
| 401 / 403 | Wrong/revoked API key, or the agent's API access toggle is off (Publish tab). |
| 400 | Missing required field — often a required context variable, or a non-E.164 number. |
| 404 | Wrong agent_id / session_id, or the session already ended. |
Successful responses are unwrapped from the {"success", "data"} envelope —
you get the data dict directly.
Test: python -m unittest discover tests.
What is Telenow?
Telenow is a voice AI platform for building production-grade phone and web agents. Pick a brain from the built-in LLM/STT/TTS providers (or bring your own model and carrier), give the agent a prompt, tools, and knowledge, and put it on a phone number, your website, or your app. Every call comes with recordings, transcripts, analytics, warm transfer to humans, outbound campaigns, and webhooks.
- Website: telenow.ai
- Documentation: telenow.ai/docs
- This SDK's guide: telenow.ai/docs/sdk-server
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file telenow-0.1.2.tar.gz.
File metadata
- Download URL: telenow-0.1.2.tar.gz
- Upload date:
- Size: 9.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8377e750f7cae3dbde20ab0fd850401997373ab0baae2089f1e2d99c8cd26546
|
|
| MD5 |
1adc990005359e92320ea93cf0e3f12a
|
|
| BLAKE2b-256 |
10692acf53470eeb919d106b05993eee0d9f6b5e693583218327666cb43c53f1
|
File details
Details for the file telenow-0.1.2-py3-none-any.whl.
File metadata
- Download URL: telenow-0.1.2-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c3ada8f0eefecce78c423846d0e4d8f2028d3912c4e6c21f5e41c78c2a92775
|
|
| MD5 |
e4226a4c46d20d9ed3f5a2a5dab051ca
|
|
| BLAKE2b-256 |
e527fdd795ff9e11166327009209a51445e8c249e1039abb076750b342105642
|