All methods live on telequick.client.TeleQuickClient.

Constructor

TeleQuickClient(
    endpoint: str,
    service_account_path: str,
    lib_path: Optional[str] = None,
)
ArgumentNotes
endpointquic://host:port. ALPN h3 is added automatically.
service_account_pathPath to your service-account JSON.
lib_pathOverride path to telequick_core_ffi.so. Defaults to the env var TELEQUICK_LIB_PATH then a packaged binary.
The constructor:
  • Loads the FFI symbols.
  • Reads the JWT signing key (lazy — actual signing is done in connect_async).
  • Generates a UUID into client.client_id for EventStreamRequest routing.

Connection

connect_async() (async context manager)

async with client.connect_async():
    ...
Opens the QUIC session, sends the initial EventStreamRequest, and yields. On exit, closes the session. You must be inside this block to call any RPC method.

Callbacks

client.on_audio_frame: Callable[[bytes], None]
client.on_call_event:  Callable[[bytes], None]
Set these before entering connect_async(). They receive the raw serde-envelope payload (no length/method prefix). Decode with deserialize_audio_frame / deserialize_call_event.

RPC methods

All RPC methods are coroutines. Calling them without await is a no-op.

await client.dial(...)

await client.dial(
    to: str,
    trunk_id: str,
    call_from: str = "",
    max_duration_ms: int = 0,
    default_app: int = DialplanAction.PARK,    # = 1
    default_app_args: str = "",
    ai_websocket_url: str = "",
    ai_quic_url: str = "",
    auto_barge_in: bool = False,
    barge_in_patience_ms: int = 250,
    client_id: Optional[str] = None,
)
Maps to Originate. client_id defaults to the SDK-generated UUID, which is also what connect_async subscribed events under — leave it alone unless you know why you’re routing events somewhere else.

await client.originate_bulk(...)

await client.originate_bulk(
    csv_url: str,
    trunk_id: str,
    calls_per_second: int,
    max_concurrent: int,
    campaign_id: str,
    default_app: int = 1,
    default_app_args: str = "",
    ai_websocket_url: str = "",
    ai_quic_url: str = "",
    auto_barge_in: bool = False,
    barge_in_patience_ms: int = 250,
)
Kicks off a CSV-driven dialer campaign. Use abort_bulk(campaign_id) to cancel.

await client.terminate(call_sid)

Hang up a single call.

await client.barge(call_sid)

Trigger or arm AI barge-in for call_sid. While the AI agent is speaking on the call, the gateway listens to the human leg and gates the AI’s audio the moment the human starts talking. This is not the supervisor-injection feature found in legacy PBXs. See Method IDs → Barge.

await client.stream_events(client_id)

Manually re-subscribe events under a different client_id. connect_async already does this for you with client.client_id — only call this if you need to fan-out events to a different SDK process.

await client.push_audio(call_sid, pcm_data)

Serialize one AudioFrame (codec PCMU, sequence_number=0, eos=False) and write it on the lazy-opened audio uni-stream. Pass raw payload bytes.

Inbound-routing RPCs

await client.set_inbound_routing(
    trunk_id="default",
    rule=3,                                           # InboundRule.HANDLE_AI
    ai_websocket_url="wss://my-app.example.com/agent",
)
await client.get_incoming_calls(trunk_id="default")  # results stream as events
await client.answer_incoming_call(
    call_sid=incoming_sid,
    ai_websocket_url="wss://my-app.example.com/agent",
)
Rule values are listed in InboundRule.

Bulk + bucket admin

await client.abort_bulk(campaign_id)
await client.get_bucket_calls(bucket_id)             # results stream as events
await client.execute_bucket_action(bucket_id, action)# action = BucketAction enum

Mid-call control (ExecuteDialplan verbs)

The 6 call-control verbs are exposed as convenience wrappers; each maps to execute_dialplan(call_sid, action, app_args) with the matching DialplanAction enum value.
await client.transfer(call_sid, "+15551234567")    # RFC 3515 blind REFER
await client.mute(call_sid)                         # gateway-side TX silence
await client.mute(call_sid, on_wire=True)           # also send recvonly re-INVITE
await client.unmute(call_sid)
await client.hold(call_sid)
await client.unhold(call_sid)
await client.send_dtmf(call_sid, "5", mode="rfc2833", duration_ms=200)
Or call execute_dialplan directly when you need an enum value the sugar doesn’t cover:
from telequick.method_id import DialplanAction
await client.execute_dialplan(call_sid, DialplanAction.PLAYBACK, "/audio/hold.wav")

Helpers

client.serialize_audio_frame(call_sid, pcm_raw, codec, sequence_number, end_of_stream) -> bytes
client.deserialize_audio_frame(payload_bytes) -> bytes
client.deserialize_call_event(payload_bytes) -> Tuple[str, str]   # (call_sid, status) — back-compat
client.parse_call_event(payload_bytes) -> CallEvent               # full 20-field dataclass
parse_call_event returns a frozen CallEvent dataclass with call_sid, event_type, status, start_timestamp_ms, q850_cause, recording_url, duration_seconds, answer_timestamp_ms, end_timestamp_ms, packets_sent, packets_received, packets_lost, bytes_sent, jitter_ms, estimated_mos, trunk_id, tenant_id, codec, timestamp_ms, client_id. See CallEvent fields. serialize_audio_frame returns a fully-framed RPC packet (length prefix + method id + envelope) — useful if you want to write to a custom QUIC stream yourself.

Enums

MethodID

from telequick.method_id import MethodID

MethodID.ORIGINATE
MethodID.AUDIO_FRAME
MethodID.STREAM_EVENTS
MethodID.EXECUTE_DIALPLAN
# ...
Values are stable across versions — see Method IDs.

DialplanAction

from telequick.method_id import DialplanAction

DialplanAction.AI_BIDIRECTIONAL_STREAM    # 6 — typical default_app on Dial
DialplanAction.TRANSFER                   # 7 — call-control verbs (only via ExecuteDialplan)
DialplanAction.MUTE                       # 8
DialplanAction.HOLD                       # 10
DialplanAction.SEND_DTMF                  # 12
Full enum: see DialplanAction values.

Errors

ErrorCause
FileNotFoundError(... FFI Core)Native lib not on disk. Set TELEQUICK_LIB_PATH.
RuntimeError("Must connect first")Calling push_audio outside connect_async() block.
RuntimeError("Must execute dial() within 'async with client.connect_async()' context.")Calling any RPC outside the connect block.
QUIC ConnectionRefusedErrorEndpoint unreachable, wrong port, or ALPN mismatch.