dial_sdk

Submodules

Attributes

Classes

AudioCallConnected

AudioCallEnded

AudioDtmf

AudioDurationWarning

AudioMark

AudioMedia

Billing

BillingActivityItem

One activity-ledger item from GET /api/v1/billing/activity. type selects

BillingActivityPage

One page of activity, newest-first. has_more signals further pages.

BillingDeposit

BillingNumber

BillingPaymentMethod

BillingPricing

BillingSubscription

Call

CallConnected

CallStatus

Server-derived, client-facing call status (camelCase, as returned).

CallStatusChanged

dict() -> new empty dictionary

CallTranscribed

dict() -> new empty dictionary

DialClient

DialConfig

EventsConnection

A long-lived subscription to the account's event stream, consumed as an

Interrupt

MakeCallParams

MediaItem

Raw bytes to upload as a media attachment; the MIME type is required.

Message

MessageMediaItem

A media attachment on a message, hosted by Dial.

PhoneNumber

PingPong

PurchaseNumberParams

ReminderRequired

Response

ResponseRequired

SendMessageParams

TranscriptItem

TranscriptUpdate

TypingSession

A scoped typing indicator — see DialClient.typing().

Functions

parse_dial_audio_message(→ DialAudioMessage)

Parse + validate a frame received from Dial. Raises ValidationError.

parse_dial_message(→ DialServerMessage)

Parse + validate a frame received from Dial. Raises ValidationError.

serialize_server_audio_message(→ str)

Validate + serialize an outbound frame to a JSON string.

serialize_server_message(→ str)

Validate + serialize an outbound frame to a JSON string (omitting unset

verify_dial_signature(→ bool)

Verify a Dial X-Dial-Signature: t=<unix_seconds>,v1=<hex> header.

Package Contents

class dial_sdk.AudioCallConnected

Bases: pydantic.BaseModel

call_id: str
direction: Literal['inbound', 'outbound']
formats: AudioFormats
from_: str
instruction: str | None = None
language: str | None = None
model_config
reconnect: bool = False
to: str
type: Literal['call_connected']
class dial_sdk.AudioCallEnded

Bases: pydantic.BaseModel

reason: CallEndReason
type: Literal['call_ended']
class dial_sdk.AudioDtmf

Bases: pydantic.BaseModel

digit: str
type: Literal['dtmf']
class dial_sdk.AudioDurationWarning

Bases: pydantic.BaseModel

seconds_remaining: int
type: Literal['duration_warning']
class dial_sdk.AudioMark

Bases: pydantic.BaseModel

name: str
type: Literal['mark']
class dial_sdk.AudioMedia

Bases: pydantic.BaseModel

payload: str
seq: int | None = None
type: Literal['media']
class dial_sdk.Billing
classmethod from_api(d: dict) Billing
balance_cents: int
deposits: list[BillingDeposit]
numbers: list[BillingNumber]
numbers_release_at: str | None
payment_methods: list[BillingPaymentMethod]
pricing: BillingPricing
subscription: BillingSubscription | None
class dial_sdk.BillingActivityItem

One activity-ledger item from GET /api/v1/billing/activity. type selects which fields are populated: - ‘usage’ → fare_name, number, billed_quantity, total_cents, attribution, phone_number_id, call_id, message_id - ‘credit’ → amount_cents, kind, invoice_id - ‘payment’ → amount_cents, invoice_id, reason

classmethod from_api(d: dict) BillingActivityItem
amount_cents: int | None = None
attribution: str | None = None
billed_quantity: int | None = None
call_id: str | None = None
fare_name: str | None = None
invoice_id: str | None = None
kind: str | None = None
message_id: str | None = None
number: str | None = None
occurred_at: str
phone_number_id: str | None = None
reason: str | None = None
total_cents: int | None = None
type: str
class dial_sdk.BillingActivityPage

One page of activity, newest-first. has_more signals further pages.

classmethod from_api(d: dict) BillingActivityPage
data: list[BillingActivityItem]
has_more: bool
class dial_sdk.BillingDeposit
classmethod from_api(d: dict) BillingDeposit
amount_cents: int
created_at: str
invoice_id: str | None = None
kind: str
class dial_sdk.BillingNumber
classmethod from_api(d: dict) BillingNumber
id: str
mode: str
nickname: str | None = None
number: str
class dial_sdk.BillingPaymentMethod
classmethod from_api(d: dict) BillingPaymentMethod
brand: str
email: str | None
exp_month: int
exp_year: int
id: str
is_default: bool
last4: str
type: str
class dial_sdk.BillingPricing
classmethod from_api(d: dict) BillingPricing
annual_cents: int
monthly_cents: int
class dial_sdk.BillingSubscription
classmethod from_api(d: dict) BillingSubscription
cancel_at_period_end: bool
interval: str
period_end: str
period_start: str
quantity: int
class dial_sdk.Call
classmethod from_api(d: dict) Call
call_started_at: str | None
cancel_requested_at: str | None
created_at: str
direction: str
duration: int
from_: str
id: str
instruction: str | None
phone_number_id: str
queued_at: str | None
started_ringing_at: str | None
status: CallStatus
terminated_at: str | None
termination_type: str | None
to: str
transcript: str | None
transfer_to: str | None
transferred_at: str | None
class dial_sdk.CallConnected

Bases: pydantic.BaseModel

call_id: str
direction: Literal['inbound', 'outbound']
from_: str
instruction: str | None = None
language: str | None = None
model_config
to: str
type: Literal['call_connected']
class dial_sdk.CallStatus

Bases: TypedDict

Server-derived, client-facing call status (camelCase, as returned).

Initialize self. See help(type(self)) for accurate signature.

cancelPending: bool
cancelRequested: bool
label: str
state: str
terminationType: str | None
class dial_sdk.CallStatusChanged

Bases: TypedDict

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object’s

(key, value) pairs

dict(iterable) -> new dictionary initialized as if via:

d = {} for k, v in iterable:

d[k] = v

dict(**kwargs) -> new dictionary initialized with the name=value pairs

in the keyword argument list. For example: dict(one=1, two=2)

Initialize self. See help(type(self)) for accurate signature.

createdAt: str
data: CallStatusChangedData
id: str
object: Literal['event']
relatedObject: RelatedObject | None
type: Literal['call.status_changed']
version: int
class dial_sdk.CallTranscribed

Bases: TypedDict

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object’s

(key, value) pairs

dict(iterable) -> new dictionary initialized as if via:

d = {} for k, v in iterable:

d[k] = v

dict(**kwargs) -> new dictionary initialized with the name=value pairs

in the keyword argument list. For example: dict(one=1, two=2)

Initialize self. See help(type(self)) for accurate signature.

createdAt: str
data: CallTranscribedData
id: str
object: Literal['event']
relatedObject: RelatedObject | None
type: Literal['call.transcribed']
version: int
class dial_sdk.DialClient(config: dial_sdk.types.DialConfig)
async close()
async get_billing() dial_sdk.types.Billing

dial billing — wallet balance, subscription, per-number mode, recent credits, payment methods.

async get_call(call_id: str) dial_sdk.types.Call

dial call get <id> — fetch a single call by id.

async list_billing_activity(*, filter: str = 'all', limit: int | None = None, starting_after: str | None = None) dial_sdk.types.BillingActivityPage

One page of the activity ledger (usage + credits + subscription payments), newest-first. Stripe-style cursor: pass starting_after set to the occurred_at of the last item to fetch the next page; has_more signals more.

async list_calls(*, number_id: str | None = None, direction: str | None = None, since: str | None = None) list[dial_sdk.types.Call]

dial call list — list calls, optionally filtered.

async list_messages(*, number_id: str | None = None, direction: str | None = None, since: str | None = None) list[dial_sdk.types.Message]

dial message list — list messages, optionally filtered.

async list_numbers() list[dial_sdk.types.PhoneNumber]

dial number list — list the account’s phone numbers.

async make_call(params: dial_sdk.types.MakeCallParams) dial_sdk.types.Call

dial call — place an outbound AI voice call.

new_events_connection() dial_sdk.events.EventsConnection

Open a long-lived connection to the account’s event stream.

SDK equivalent of dial wait-for: rather than returning a single event, it yields every message.received / call.status_changed / call.ended / call.transcribed event on the account channel. The PubNub token is re-minted automatically before it expires. Every event shares one envelope; read values from event["data"]. No I/O happens until the connection is entered:

async with client.new_events_connection() as conn:
    async for event in conn:
        if event["type"] == "message.received":
            print(event["data"]["from"], event["data"]["body"])
async purchase_number(params: dial_sdk.types.PurchaseNumberParams) dial_sdk.types.PhoneNumber

dial number purchase — provision a new phone number.

async reply_to_message(message_id: str, *, body: str | None = None, reaction: str | None = None) dial_sdk.types.Message

dial message reply — reply or react to an existing message.

Exactly one of body (threaded reply) or reaction (a reaction name — love/like/dislike/laugh/emphasize/question — or a single emoji) is required. The server derives the sender and recipient from the target message, so there is no to/from_number_id.

async send_message(params: dial_sdk.types.SendMessageParams) dial_sdk.types.Message

dial message — send a message, optionally with media attachments (MMS).

async set_inbound_instruction(number_id: str, inbound_instruction: str) dial_sdk.types.PhoneNumber

Deprecated: use set_number_properties() instead.

async set_number_properties(number_id: str, *, inbound_instruction: str | None = None, inbound_voice_gender: str | None = _UNSET, inbound_language: str | None = _UNSET, nickname: str | None = _UNSET, max_call_duration_seconds: int | None = None, first_name: str | None = _UNSET, last_name: str | None = _UNSET, avatar: str | Path | MediaItem | None = None) dial_sdk.types.PhoneNumber

dial number set — update a number’s properties (any subset; at least one).

nickname=None or nickname="" clears the nickname; omit the argument to leave it unchanged. inbound_voice_gender=None clears the inbound voice (reverts to the default, female); pass “male”/”female” to set it. inbound_language=None clears the inbound language (reverts to per-call detection from the caller’s country prefix); pass a BCP-47 tag (e.g. “es-ES”) to pin inbound calls to one language.

first_name / last_name set the iMessage display identity — the name shown beside the number’s messages in recipients’ Messages apps (max 30 chars; None or "" clears; iMessage numbers only — rejected with 400 otherwise). avatar sets the identity photo: an http(s) URL string (downloaded server-side), a local image Path, or a MediaItem of raw bytes (both uploaded as multipart). jpeg/png/gif/webp, max 5 MB. Replace-only — a photo can be replaced but not removed.

async start_typing(*, to_number: str, from_number: str) None

dial typing start — show a typing indicator to the recipient.

iMessage numbers display it; standard (SMS) numbers have no typing concept and silently ignore it, so calling this unconditionally is safe. Fire-and-forget and free. Delivering a message or reaction clears the indicator natively on the recipient’s device — start again after a send to keep composing, and pair with stop_typing when you stop without sending (or use typing(), whose session renews it after each send and stops it on exit). from_number is a flexible ref: a phone-number id, one of your numbers in E.164, or a nickname.

async stop_typing(*, to_number: str, from_number: str) None

dial typing stop — clear a typing indicator shown with start_typing.

typing(*, to_number: str, from_number: str) TypingSession

Scope a typing indicator to a block: starts on enter, stops on exit.

async with dial.typing(to_number="+1…", from_number="Support line") as session:
    await session.send_message(body="…")  # to/from prefilled

Delivering a message clears the indicator natively on the recipient’s device, so the session renews it after each send_message — the indicator persists until __aexit__, the only real stop. Teardown is best-effort: a failure to clear the indicator never masks an exception raised inside the block (and is swallowed on a clean exit too). There is no keep-alive; the recipient’s device may drop a stale indicator during a very long pause.

class dial_sdk.DialConfig
api_key: str
base_url: str | None = None
user_agent: str | None = None
class dial_sdk.EventsConnection(mint: MintGrant)

A long-lived subscription to the account’s event stream, consumed as an async iterator of event dicts.

This is the SDK’s idiom for what the CLI does one-shot with dial wait-for: instead of returning a single event, it yields every message.received / call.status_changed / call.ended / call.transcribed event on the account channel until the consumer stops iterating or the connection is closed. The PubNub token is silently re-minted before it expires, so a connection can stay open indefinitely. PubNub specifics (subscribe key, channel, token, TTL) are never surfaced to the caller.

Usage:

async with client.new_events_connection() as conn:
    async for event in conn:
        handle(event)
async close() None

Tear down the subscription and end iteration. Idempotent.

async open() EventsConnection

Mint the first token and establish the subscription. Idempotent.

class dial_sdk.Interrupt

Bases: pydantic.BaseModel

content: str
content_complete: bool
end_call: bool | None = None
type: Literal['interrupt']
class dial_sdk.MakeCallParams
from_number: str | None = None
from_number_id: str | None = None
idempotency_key: str | None = None
language: str | None = None
max_call_duration_seconds: int | None = None
outbound_instruction: str = ''
to: str
transfer_to: str | None = None
voice_gender: str | None = None
class dial_sdk.MediaItem

Raw bytes to upload as a media attachment; the MIME type is required.

content_type: str
data: bytes
filename: str = 'media'
class dial_sdk.Message
classmethod from_api(d: dict) Message
body: str
channel: str
created_at: str
direction: str
from_: str
id: str
media: list[MessageMediaItem] = []
phone_number_id: str
reaction: str | None = None
reply_to_id: str | None = None
status: str
status_error: str | None = None
to: str
class dial_sdk.MessageMediaItem

A media attachment on a message, hosted by Dial.

classmethod from_api(d: dict) MessageMediaItem
content_type: str
id: str
original_url: str | None
url: str
class dial_sdk.PhoneNumber
classmethod from_api(d: dict) PhoneNumber
account_id: str
avatar_url: str | None = None
capabilities: list[str]
country: str
created_at: str
first_name: str | None = None
id: str
inbound_instruction: str | None
inbound_language: str | None
inbound_voice_gender: str | None
last_name: str | None = None
max_call_duration_seconds: int | None
nickname: str | None
number: str
setup_error: str | None
setup_status: str
class dial_sdk.PingPong

Bases: pydantic.BaseModel

timestamp: int
type: Literal['ping_pong']
class dial_sdk.PurchaseNumberParams
area_code: str | None = None
inbound_instruction: str
inbound_language: str | None = None
inbound_voice_gender: str | None = None
include_imessage: bool = False
class dial_sdk.ReminderRequired

Bases: pydantic.BaseModel

response_id: int
transcript: list[TranscriptItem]
type: Literal['reminder_required']
class dial_sdk.Response

Bases: pydantic.BaseModel

content: str
content_complete: bool
end_call: bool | None = None
response_id: int
type: Literal['response']
class dial_sdk.ResponseRequired

Bases: pydantic.BaseModel

response_id: int
transcript: list[TranscriptItem]
type: Literal['response_required']
class dial_sdk.SendMessageParams
body: str = ''
channel: str | None = None
force_audio_file: bool = False
from_number: str | None = None
from_number_id: str | None = None
media: list[SendMessageMedia] = []
to: str
class dial_sdk.TranscriptItem

Bases: pydantic.BaseModel

content: str
role: Role
class dial_sdk.TranscriptUpdate

Bases: pydantic.BaseModel

transcript: list[TranscriptItem]
type: Literal['transcript_update']
class dial_sdk.TypingSession(client: DialClient, *, to_number: str, from_number: str)

A scoped typing indicator — see DialClient.typing().

async send_message(**kwargs) dial_sdk.types.Message

Send a message in this conversation — to and the from-number ref are prefilled.

Accepts the same keyword fields as SendMessageParams minus the addressing (to/from_number/from_number_id). The delivered message clears the indicator natively on the recipient’s device, so the session renews it right after the send — the indicator persists until __aexit__.

dial_sdk.parse_dial_audio_message(raw: str | bytes | dict) DialAudioMessage

Parse + validate a frame received from Dial. Raises ValidationError.

dial_sdk.parse_dial_message(raw: str | bytes | dict) DialServerMessage

Parse + validate a frame received from Dial. Raises ValidationError.

dial_sdk.serialize_server_audio_message(message: ServerAudioMessage) str

Validate + serialize an outbound frame to a JSON string.

dial_sdk.serialize_server_message(message: ServerDialMessage) str

Validate + serialize an outbound frame to a JSON string (omitting unset end_call).

dial_sdk.verify_dial_signature(secret: str, header: str, data: str, *, now: int | None = None, tolerance_seconds: int = 300) bool

Verify a Dial X-Dial-Signature: t=<unix_seconds>,v1=<hex> header.

The signature is HMAC-SHA256(secret, "<t>.<data>"), where data is the value the signature covers — the call_id for the self-hosted WebSocket protocol, or the raw request body for a webhook delivery. Returns False (never raises) on a malformed header, a stale/future timestamp, or a mismatch. Matches Dial’s server-side signer byte-for-byte.

dial_sdk.CallEvent
dial_sdk.DialEvent
dial_sdk.MessageEvent
dial_sdk.SendMessageMedia