This release is a pre-release and may not be stable for production use.
Dialgem
The official Python SDK for the Dialgem voice AI API.
Build voice agents, put them on phone numbers, place calls, steer them live, and read transcripts, all from your backend.
Website · Issues · Changelog · API surface
from dialgem import Dialgem
dialgem = Dialgem() # reads DIALGEM_API_KEY
agent = dialgem.agents.create(name="Front desk", system_prompt="You answer for Acme Dental.")
call = dialgem.calls.create_phone_call(to="+14155550123", from_="+14155550100", agent_id=agent.id)
Why this SDK
- Sync and async, one surface.
DialgemandAsyncDialgemexpose the same methods. - Typed end to end. Request bodies are
TypedDicts generated from the API schema; responses are pydantic v2 models, so your editor andmypyknow every field. - Safe by default.
POSTis never retried (a retry could ring someone twice),GET/PUT/PATCH/DELETEretry with backoff andRetry-After, and an API change never crashes your integration: unknown or mismatched fields come back as-is with one warning. - Everything a backend needs. Agents (with tools, knowledge and MCP servers), phone numbers, calls and chats, saved flows and LLMs, contacts, simulations, web calls, live call control, and webhook signature verification.
- Small. Python 3.10+, three dependencies:
httpx,pydantic,typing_extensions. - Server-side only. An organization API key grants full access to your organization. To let a visitor talk to an agent from a web page, see Web calls.
Contents
Install · Quickstart · Async · Recipes · Live call control · Errors · Retries · Responses · Development · License
Which routes this SDK can and cannot reach, and why: docs/api-surface.md.
Install
pip install dialgem
Quickstart
Create an organization API key (zk_…) in the Dialgem dashboard and keep it in your server's environment.
from dialgem import Dialgem
dialgem = Dialgem() # reads DIALGEM_API_KEY
agent = dialgem.agents.create(
name="Front desk",
system_prompt="You are the front desk for {{business_name}}.",
first_message="Thanks for calling, how can I help?",
default_dynamic_variables={"business_name": "Acme Dental"},
)
| Option | Default | |
|---|---|---|
api_key |
$DIALGEM_API_KEY |
Organization API key. |
timeout |
60 |
Seconds per attempt. |
max_retries |
2 |
See Retries. |
default_headers |
{} |
Sent with every request. |
http_client |
a new httpx.Client |
Your own client (proxy, mTLS, instrumentation). You close it. |
Every method also takes request_options={"timeout": 10, "max_retries": 0, "headers": {...}} for that one request.
Field names are the API's own snake_case, passed as keyword arguments and sent exactly as written.
Async
import asyncio
from dialgem import AsyncDialgem
async def main():
async with AsyncDialgem() as dialgem:
agent = await dialgem.agents.create(name="Front desk")
async for call in dialgem.calls.list(limit=50): # walks every page
print(call.id, call.call_status)
asyncio.run(main())
Recipes
Provision an agent on a phone number
agent = dialgem.agents.create(name="Front desk", system_prompt="…")
number = dialgem.phone_numbers.create(phone_number="+14155550100")
dialgem.phone_numbers.update(
number.id,
inbound_agent_id=agent.id,
set_inbound_agent=True, # inbound_agent_id is ignored without this
)
Agents also have tools, knowledge and MCP servers:
dialgem.agents.functions.create(
agent.id,
name="lookup_order",
url="https://example.com/api/orders/lookup",
parameters={"type": "object", "properties": {"order_id": {"type": "string"}}},
)
dialgem.agents.knowledge.create(agent.id, filename="faq.txt", content=faq_text)
Place an outbound call
call = dialgem.calls.create_phone_call(
to="+14155550123",
from_="+14155550100", # `from_`: the wire name; must be a number on your organization
agent_id=agent.id,
metadata={"order": "A-1042"},
dialgem_llm_dynamic_variables={"customer_name": "Ada"},
)
create_phone_call is a POST and is not retried. A retry after an ambiguous 502 could ring the person twice.
List calls
for call in dialgem.calls.list(agent_id=agent.id, start_after=1730000000000, limit=50):
print(call.id, call.transcript_text)
page = dialgem.calls.list(limit=50) # or one page at a time
page.data, page.has_next_page(), page.get_next_page()
Web calls
Your server mints a one-use token; the page connects with @dialgem/client using that token alone, so your API key never reaches the browser.
# once: allow the page's origin
dialgem.web_calls.update_settings(allowed_origins=["https://app.example.com"])
# per visitor, from your own endpoint
web_call = dialgem.web_calls.create(agent_id="AGENT_ID", metadata={"visitor": "v_123"})
return {"access_token": web_call.access_token} # 60 seconds, single use
Verify webhooks
Verify against the raw request body exactly as received. A body parsed and re-serialized no longer matches.
from dialgem import webhooks, WebhookVerificationError
# FastAPI
@app.post("/dialgem")
async def hook(request: Request):
body = await request.body()
try:
event = webhooks.construct_event(body, request.headers.get("x-dialgem-signature"), SECRET)
except WebhookVerificationError:
return Response(status_code=400)
if event.event == "call_ended":
print(event.call.id, event.call.duration_ms)
return Response(status_code=204)
construct_function_call does the same for the requests Dialgem sends to your agent's custom functions. Signatures carry a timestamp and are rejected after five minutes (tolerance_ms=).
Live call control
Steer a call while it is happening. session_id is the session's id from sessions.list() or the call's own call_id, and the call can be on any service instance.
# Change this call's prompt or variables from the agent's next turn (not saved, only this call).
dialgem.sessions.update(call.id, dynamic_variables={"plan": "pro"})
# Make the agent say a line in its own voice; interrupt=True cuts in now.
dialgem.sessions.say(call.id, text="One moment while I check that.")
# Give a taken-over call back to the agent.
dialgem.sessions.release(call.id)
say and release are off unless your deployment enables live control (PermissionDeniedError with err_code == "live_control_disabled"), and are for owners, admins and API keys only. Listening in and taking a call over run over a WebSocket (/v1/agent/sessions/{id}/live), which this SDK does not wrap.
Errors
from dialgem import NotFoundError, APIError, APIConnectionError
try:
dialgem.agents.retrieve("nope")
except NotFoundError as e:
print(e.status, e.err_code, e.request_id) # quote request_id to support
except APIError as e:
print(e.status, e.issues) # e.g. 422 validation issues
except APIConnectionError:
... # no response (APITimeoutError is a subclass)
400 BadRequestError, 401 AuthenticationError, 403 PermissionDeniedError, 404 NotFoundError, 409 ConflictError, 422 UnprocessableEntityError, 429 RateLimitError, 5xx InternalServerError. All derive from APIError → DialgemError.
Retries
GET, PUT, PATCH and DELETE retry up to max_retries times on 408, 429, 5xx and connection errors, with exponential backoff and jitter, honouring Retry-After / Retry-After-Ms (capped at 60 s). POST is never retried unless you pass request_options={"max_retries": n}. 409 is never retried. File uploads are never retried.
Responses
Responses are pydantic models. Unknown fields are kept (as attributes and in model.model_extra), and model.to_dict() returns the JSON as the API sent it. If the API returns something that does not fit the SDK's model (a field changed type or went missing), the SDK returns it unvalidated and issues one UserWarning per model instead of raising, so an API change never takes your integration down. Upgrade the SDK when you see it.
Development
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
mypy src && ruff check .
python scripts/unasync.py # regenerate src/dialgem/aresources from src/dialgem/resources
python scripts/gen_params.py # regenerate request types from openapi/openapi.json
python scripts/mutate.py # mutation spot-check of the safety rules
src/dialgem/aresources/ and src/dialgem/types/params.py are generated; edit the sync resources and the OpenAPI snapshot, never the generated files. CI fails if they are stale.
License
Apache-2.0
If this SDK saves you time, a ⭐ on GitHub helps other developers find it.
Metadata
Release files for dialgem 0.1.0b2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| dialgem-0.1.0b2.tar.gz | 59.2 kB | With provenance | Details for dialgem-0.1.0b2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dialgem-0.1.0b2-py3-none-any.whl | Python 3 | none | any | Details for dialgem-0.1.0b2-py3-none-any.whl |
Total release size: 140.7 kB
Release files / dialgem-0.1.0b2.tar.gz
| Download URL | dialgem-0.1.0b2.tar.gz |
|---|---|
| Size | 59.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6e11dd24a1a7d333024bd9c51e097c9157bdfe1be1b1f7302de7b83978465731
|
|
BLAKE2b-256 checksum How to use checksums |
50b7e6c191cc7c67bbeb44b50091877a1c438f378d440d931b2e663abcdccc06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logRelease files / dialgem-0.1.0b2-py3-none-any.whl
| Download URL | dialgem-0.1.0b2-py3-none-any.whl |
|---|---|
| Size | 81.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4aede54698814e34483583e4801754df6f0ca952d42582b021d5187d6716184e
|
|
BLAKE2b-256 checksum How to use checksums |
83ccf5ec0f0ad0f34dccbb44d84f5d20455e959082121f01b7525e53c15da5f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logSecurity for dialgem 0.1.0b2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for dialgem-0.1.0b2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 7, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.