Python SDK
Async Python SDK for the Audact compliance validation platform. Covers all API operations with full type safety via pydantic and httpx.
Async Python SDK for the Audact compliance validation platform.
Requirements
Installation
The Python SDK is not published yet. Installation instructions will appear here when it is.
Quick start
python
import asyncio
import uuid
from audact import AudactClient
async def main():
async with AudactClient(api_key="aud_live_...") as client:
# Validate a conversation turn
result = await client.validate(
call_id=uuid.uuid4(),
tenant_id=uuid.UUID("..."),
config_id=uuid.UUID("..."),
turn=1,
content="Hello, this is an automated message.",
)
print(result.verdict) # "ALLOW" | "BLOCK" | "MODIFY"
asyncio.run(main())Base URL
Point the client at one host with base_url; every service resolves to it
and the SDK adds the /v1/... paths itself (a trailing / or /v1 on
base_url is stripped):
python
client = AudactClient(api_key="aud_live_...", base_url="https://api.audact.ai")
Override the base URL via the environment variable with no code change:
bash
export AUDACT_BASE_URL=https://api.audact.ai
Resolution order per service: <service>_url= kwarg → AUDACT_<SERVICE>_URL
env → base_url= kwarg → AUDACT_BASE_URL env.
Per-service URLs are only needed for deployments where each service has its
own host (see Configuration).
Configuration
Override service base URLs via constructor keyword arguments or environment variables:
| Environment variable | Default |
|---|---|
AUDACT_API_KEY | (empty) |
AUDACT_BASE_URL | (unset) — one host for every service (see Base URL) |
AUDACT_POLICY_ENGINE_URL | falls back to AUDACT_BASE_URL |
AUDACT_VALIDATOR_URL | falls back to AUDACT_BASE_URL |
AUDACT_EVIDENCE_LAYER_URL | falls back to AUDACT_BASE_URL |
AUDACT_LLM_PROXY_URL | falls back to AUDACT_BASE_URL |
AUDACT_TENANT_MANAGEMENT_URL | falls back to AUDACT_BASE_URL |
AUDACT_AUDIO_VALIDATOR_URL | falls back to AUDACT_BASE_URL |
AUDACT_AGGREGATOR_URL | falls back to AUDACT_BASE_URL |
AUDACT_VOICE_GATEWAY_URL | falls back to AUDACT_BASE_URL |
AUDACT_AGENT_WORKER_URL | falls back to AUDACT_BASE_URL |
AUDACT_ORG_MANAGEMENT_URL | falls back to AUDACT_BASE_URL |
AUDACT_ORG_TOKEN | (empty) — OrgBearer JWT for client.org.* |
max_retries (constructor kwarg, default 0) opts into a bounded
retry-with-backoff for idempotent GETs and 429/5xx responses — see
Retries below.
Coverage
The SDK is at full parity with the documented API: 209 of 211 operations are covered. The remaining 2 are receive-side
provider-to-platform webhooks — POST /telnyx/webhook and
POST /webhooks/livekit — which are ingress endpoints Audact receives,
never calls.
The SDK is monolithic with attribute sub-clients: every pre-existing
flat method on AudactClient (e.g. client.validate, client.evaluate,
client.create_tenant) keeps working unchanged, and the large new surfaces
added for parity are exposed as client.<name>.* sub-clients:
| Sub-client | Area | Auth | What it covers |
|---|---|---|---|
| (flat methods) | Validation | API key | Per-turn ACVF validation |
| (flat methods) | Policy | API key | Policy evaluation, jurisdiction policy documents |
| (flat methods) | Evidence | API key | Evidence events, chains, verify/reconstruct, calls, violations, Article 50 reports/exports, C2PA, receipts, delivery, transcript reconciliation |
| (flat methods) | LLM Proxy | API key | Compliance-validated LLM chat completion (sync + streaming) |
| (flat methods) | Tenant Management | API key / platform token | Tenant CRUD, API keys, configurations, KYC documents/sets, platform export/import (export/import require a platform_token) |
| (flat methods) | Voice Gateway | API key | Number provisioning order flow + management lifecycle, bring-your-own (BYO) SIP trunk connection |
client.calls | Voice Gateway | API key | SIP call lifecycle (outbound/inbound/browser, hangup, status, handover, transfer), trunks, dispatch rules, admission URL, WS tickets |
client.webhooks | Tenant Management | API key | Webhook endpoint CRUD, test-ping, delivery history/resend, event-type/schema discovery, secret status/rotation, verify_webhook_signature helper |
client.agents | Agent Worker | API key | Agent CRUD + duplicate, TTS voice catalog CRUD, pipeline status |
client.tools | Agent Worker | API key | Function-calling tool CRUD/test, agent-tool attach/detach |
client.knowledge_bases | Tenant Management | API key | Knowledge-base (RAG) CRUD, sources (url/text, upload_kb_source_file multipart, re-ingest), agent links + retrieval tuning, crawl exclusions, test_kb_retrieval harness |
client.integrations | Agent Worker | API key | Cal.com connection CRUD/test, event-type discovery |
client.aggregation | Aggregator | API key (public: get_metachain, verify_inclusion) | Merkle aggregation proofs, public meta-chain, offline inclusion verification |
client.audio | Audio Validator | API key | Synchronous per-turn audio validation, post-call audio-to-transcript reconciliation |
client.auth | Tenant Management | API key | API-key ↔ JWT session bridge (exchange_key, refresh_token) |
client.org | Org Management | OrgBearer (JWT) | Org self-service: signup/login/refresh/logout, profile, KYC, plus the org-owned tenant lifecycle (list/create/get, soft-delete/enable/disable/restore, initialize, issue key, impersonate) |
client.pii | Evidence Layer | API key | PII keyset erasure status, incl. list_erased_keysets(call_id, keyset_ids) |
Auth flows
Most methods send Authorization: Bearer <api_key> using the api_key
passed to the AudactClient constructor. Two surfaces use a different
bearer token (org and platform):
- OrgBearer — a JWT obtained from
client.org.login(). The SDK stores it in-memory on the client automatically; every subsequentclient.org.*call sends it instead of the API key. - Platform token — a super-admin token required for
export_tenant/import_tenant. Pass it asplatform_token=to the constructor or callclient.set_platform_token(token)at any time.
python
async with AudactClient(api_key="aud_live_...") as client:
# login() is public (no bearer required) and stores the returned access
# token on client.org automatically.
await client.org.login(OrgLoginRequest(email="admin@example.com", password="hunter2"))
# Subsequent calls send `Authorization: Bearer <jwt>`.
profile = await client.org.get_profile()
tenants = await client.org.list_tenants()
# Rotate the token before it expires, then clear it when done.
await client.org.refresh()
await client.org.logout()
# Or start pre-authenticated:
client = AudactClient(
api_key="aud_live_...",
org_token="<jwt from a prior login>",
)Streaming
python
async with AudactClient(api_key="...") as client:
async for chunk in client.proxy_stream(
"/v1/proxy/openai/stream",
json={"model": "gpt-4o", "messages": [...], "stream": True},
):
print(chunk.decode(), end="", flush=True)KYC documents & number provisioning
Tenant identity is always taken from the API key — none of these methods
take a tenant_id parameter. Document uploads are multipart/form-data;
document bytes are never returned by any endpoint (metadata + sha256 only).
python
async with AudactClient(api_key="aud_live_...") as client:
# Upload a KYC document (raw bytes or an open file handle both work).
doc = await client.upload_kyc_document(
open("passport.pdf", "rb"), doc_type="passport"
)
# Discover what Telnyx requires, then create + submit a document set.
requirements = await client.get_kyc_requirements(country="NL", number_type="mobile")
doc_set = await client.create_kyc_document_set(
CreateKYCDocumentSetRequest(
name="NL mobile KYC",
country_code="NL",
number_type="mobile",
items=[
KYCDocumentSetItemRequest(requirement_id=req.id, document_id=doc.id)
for req in requirements
],
)
)
# Once doc_set.status == "approved" (poll get_kyc_document_set), place an order.
order = await client.create_number_order(
CreateNumberOrderRequest(country_code="NL", number_type="mobile", set_id=doc_set.id)
)
order = await client.wait_for_order(order.id) # polls until success/failure/cancelledBring-your-own (BYO) telephony
Tenant identity is always taken from the API key. BYO is pure JSON — no
multipart involved. SIP credentials (sip_username/sip_password) are
write-only: stored securely and never echoed back on any
response (has_credentials is a boolean flag only).
python
async with AudactClient(api_key="aud_live_...") as client:
# Fetch this deployment's SIP ingress host before configuring any trunk.
config = await client.get_byo_config()
print(config.sip_ingress_host, config.signaling_ips)
# Register a BYO SIP trunk connection (either creds or an IP allowlist
# is required) — runs a connect-time health probe before returning.
conn = await client.create_byo_connection(
CreateBYOConnectionRequest(
name="Acme HQ Twilio Trunk",
provider="twilio",
sip_host="acme.pstn.twilio.com",
sip_username="acme-trunk-user",
sip_password="correct-horse-battery-staple",
)
)
print(conn.connection_health) # "healthy" | "unreachable"
# Add a number over that connection — origin-aware create_number.
number = await client.create_number(
CreateNumberRequest(
did="+12125551234",
country_code="US",
origin="byo",
byo_connection_id=conn.id,
)
)
print(number.origin, number.provider, number.connection_health)A new sub-client: webhooks
python
async with AudactClient(api_key="aud_live_...") as client:
endpoint = await client.webhooks.create_webhook(
CreateWebhookRequest(
scope="account",
url="https://example.com/hooks/audact",
events=["call_ended", "compliance.violation_detected"],
)
)
# `endpoint.signing_secret` is returned ONLY on this first call — store it.
await client.webhooks.test_webhook(endpoint.id)
deliveries = await client.webhooks.list_webhook_deliveries(endpoint.id)Verify inbound deliveries with the bundled signature helper (constant-time, tolerant of a secret-rotation grace period):
python
from audact import verify_webhook_signature
is_valid = verify_webhook_signature(
payload_bytes=raw_request_body, # the exact, unparsed bytes
signature_header=request.headers["X-Audact-Signature"],
secret=stored_signing_secret,
)Examples
A runnable example script lives under examples/:
examples/end_to_end_flow.py— the common flow: create an agent, provision- assign a phone number via the order flow, place a browser test call, then
list/read calls. Run it with
AUDACT_API_KEYset, or dry-run it with--check(mocks every HTTP call in-process, no network access required).
- assign a phone number via the order flow, place a browser test call, then
list/read calls. Run it with
Error handling
API errors inherit from AudactAPIError; timeouts (AudactTimeoutError) and connection failures (AudactConnectionError) inherit from AudactError:
| Exception | HTTP status |
|---|---|
AudactAuthError | 401 |
AudactForbiddenError | 403 |
AudactNotFoundError | 404 |
AudactRateLimitError | 429 |
AudactTimeoutError | timeout |
AudactConnectionError | conn error |
The server wraps every error as a nested envelope,
{"error": {"code", "message", "hint", "details", "request_id"}}.
AudactAPIError parses this and exposes
code, message, details, hint, and request_id (the last also
falls back to the X-Request-ID response header) — quote request_id
when contacting support:
python
from audact import AudactAPIError, AudactRateLimitError
try:
await client.validate(...)
except AudactRateLimitError as exc:
print("retry in", exc.retry_after, "seconds", exc.request_id)
except AudactAPIError as exc:
print(exc.code, exc.message, exc.hint, exc.request_id)Retries & rate limits
AudactRateLimitError(HTTP 429) additionally exposesretry_after(parsed from theRetry-Afterheader) andrate_limit(aRateLimitInfosnapshot ofX-RateLimit-Limit/-Remaining/-Reset).client.last_rate_limitholds the most recentX-RateLimit-*snapshot seen on any response, success or error.- Retries are opt-in and off by default — pass
max_retries=Nto the constructor to enable a bounded retry-with-backoff for idempotentGETs and429/5xxresponses (honoringRetry-After). Retries are never applied to Validator-adjacent calls (/v1/validate), which have a 40ms hot-path budget.
License
MIT
