Skip to main content
Audact
See the offer

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 variableDefault
AUDACT_API_KEY(empty)
AUDACT_BASE_URL(unset) — one host for every service (see Base URL)
AUDACT_POLICY_ENGINE_URLfalls back to AUDACT_BASE_URL
AUDACT_VALIDATOR_URLfalls back to AUDACT_BASE_URL
AUDACT_EVIDENCE_LAYER_URLfalls back to AUDACT_BASE_URL
AUDACT_LLM_PROXY_URLfalls back to AUDACT_BASE_URL
AUDACT_TENANT_MANAGEMENT_URLfalls back to AUDACT_BASE_URL
AUDACT_AUDIO_VALIDATOR_URLfalls back to AUDACT_BASE_URL
AUDACT_AGGREGATOR_URLfalls back to AUDACT_BASE_URL
AUDACT_VOICE_GATEWAY_URLfalls back to AUDACT_BASE_URL
AUDACT_AGENT_WORKER_URLfalls back to AUDACT_BASE_URL
AUDACT_ORG_MANAGEMENT_URLfalls 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-clientAreaAuthWhat it covers
(flat methods)ValidationAPI keyPer-turn ACVF validation
(flat methods)PolicyAPI keyPolicy evaluation, jurisdiction policy documents
(flat methods)EvidenceAPI keyEvidence events, chains, verify/reconstruct, calls, violations, Article 50 reports/exports, C2PA, receipts, delivery, transcript reconciliation
(flat methods)LLM ProxyAPI keyCompliance-validated LLM chat completion (sync + streaming)
(flat methods)Tenant ManagementAPI key / platform tokenTenant CRUD, API keys, configurations, KYC documents/sets, platform export/import (export/import require a platform_token)
(flat methods)Voice GatewayAPI keyNumber provisioning order flow + management lifecycle, bring-your-own (BYO) SIP trunk connection
client.callsVoice GatewayAPI keySIP call lifecycle (outbound/inbound/browser, hangup, status, handover, transfer), trunks, dispatch rules, admission URL, WS tickets
client.webhooksTenant ManagementAPI keyWebhook endpoint CRUD, test-ping, delivery history/resend, event-type/schema discovery, secret status/rotation, verify_webhook_signature helper
client.agentsAgent WorkerAPI keyAgent CRUD + duplicate, TTS voice catalog CRUD, pipeline status
client.toolsAgent WorkerAPI keyFunction-calling tool CRUD/test, agent-tool attach/detach
client.knowledge_basesTenant ManagementAPI keyKnowledge-base (RAG) CRUD, sources (url/text, upload_kb_source_file multipart, re-ingest), agent links + retrieval tuning, crawl exclusions, test_kb_retrieval harness
client.integrationsAgent WorkerAPI keyCal.com connection CRUD/test, event-type discovery
client.aggregationAggregatorAPI key (public: get_metachain, verify_inclusion)Merkle aggregation proofs, public meta-chain, offline inclusion verification
client.audioAudio ValidatorAPI keySynchronous per-turn audio validation, post-call audio-to-transcript reconciliation
client.authTenant ManagementAPI keyAPI-key ↔ JWT session bridge (exchange_key, refresh_token)
client.orgOrg ManagementOrgBearer (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.piiEvidence LayerAPI keyPII 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 subsequent client.org.* call sends it instead of the API key.
  • Platform token — a super-admin token required for export_tenant / import_tenant. Pass it as platform_token= to the constructor or call client.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/cancelled

Bring-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_KEY set, or dry-run it with --check (mocks every HTTP call in-process, no network access required).

Error handling

API errors inherit from AudactAPIError; timeouts (AudactTimeoutError) and connection failures (AudactConnectionError) inherit from AudactError:

ExceptionHTTP status
AudactAuthError401
AudactForbiddenError403
AudactNotFoundError404
AudactRateLimitError429
AudactTimeoutErrortimeout
AudactConnectionErrorconn 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 exposes retry_after (parsed from the Retry-After header) and rate_limit (a RateLimitInfo snapshot of X-RateLimit-Limit/-Remaining/-Reset). client.last_rate_limit holds the most recent X-RateLimit-* snapshot seen on any response, success or error.
  • Retries are opt-in and off by default — pass max_retries=N to the constructor to enable a bounded retry-with-backoff for idempotent GETs and 429/5xx responses (honoring Retry-After). Retries are never applied to Validator-adjacent calls (/v1/validate), which have a 40ms hot-path budget.

License

MIT

Source updated: October 19, 2026