dial_sdk¶
Submodules¶
Attributes¶
Classes¶
One activity-ledger item from GET /api/v1/billing/activity. type selects |
|
One page of activity, newest-first. has_more signals further pages. |
|
Server-derived, client-facing call status (camelCase, as returned). |
|
dict() -> new empty dictionary |
|
dict() -> new empty dictionary |
|
One capability's state. |
|
One phone number the account's lines have talked to. |
|
One page of contacts, and whether older ones remain. |
|
A long-lived subscription to the account's event stream, consumed as an |
|
A group conversation — a conversation that isn't a phone number. |
|
Raw bytes to upload as a media attachment; the MIME type is required. |
|
A media attachment on a message, hosted by Dial. |
|
dict() -> new empty dictionary |
|
An advance on one of an outbound message's two independent axes. |
|
What a phone number can receive. |
|
dict() -> new empty dictionary |
|
A change to one of a phone number's capabilities. |
|
One uninterrupted stretch of speech by one party, placed in time. |
|
A scoped typing indicator — see |
Functions¶
|
Parse + validate a frame received from Dial. Raises |
|
Parse + validate a frame received from Dial. Raises |
Validate + serialize an outbound frame to a JSON string. |
|
|
Validate + serialize an outbound frame to a JSON string (omitting unset |
|
Verify a Dial |
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.AudioDurationWarning¶
Bases:
pydantic.BaseModel- seconds_remaining: int¶
- type: Literal['duration_warning']¶
- class dial_sdk.AudioMedia¶
Bases:
pydantic.BaseModel- payload: str¶
- seq: int | None = None¶
- type: Literal['media']¶
- class dial_sdk.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¶
-
- 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¶
- transcript_turns: list[TranscriptTurn] | 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:
TypedDictServer-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:
TypedDictdict() -> 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']¶
- type: Literal['call.status_changed']¶
- version: int¶
- class dial_sdk.CallTranscribed¶
Bases:
TypedDictdict() -> 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']¶
- type: Literal['call.transcribed']¶
- version: int¶
- class dial_sdk.CapabilityState¶
Bases:
TypedDictOne capability’s state.
previousStatus,errorandretryAvailableAtappear only on the asynchronous capabilities (callon an iMessage number, andwhatsapp); a synchronous one has no transition to report and cannot fail.Initialize self. See help(type(self)) for accurate signature.
- error: str | None¶
- previousStatus: str | None¶
- retryAvailableAt: str | None¶
- status: str¶
- class dial_sdk.Contact¶
One phone number the account’s lines have talked to.
Derived from message and call history rather than stored: there is no address book to add anyone to, so a contact has no id — the number is its identity.
- call_count: int¶
- last_at: str¶
- last_body: str¶
- last_call_duration: int | None¶
- last_direction: str¶
- last_kind: str¶
- last_media_count: int¶
- last_redacted: bool¶
- message_count: int¶
- number: str¶
- class dial_sdk.ContactsPage¶
One page of contacts, and whether older ones remain.
- classmethod from_api(d: dict) ContactsPage¶
- has_more: bool¶
- 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_all_contacts(page_size: int = 1000, *, number_id: str | None = None) list[dial_sdk.types.Contact]¶
Every contact, following the cursor until the API reports no more.
“Who have I talked to” is not answered by a first page, so this is usually the method you want.
number_idnarrows it to one line’s contacts. The walk is bounded (25 pages) so ahas_morethat never goes false cannot loop forever.
- 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_afterset to theoccurred_atof the last item to fetch the next page;has_moresignals more.
- async list_calls(*, number_id: str | None = None, direction: str | None = None, since: str | None = None, contact: str | None = None) list[dial_sdk.types.Call]¶
dial call list — list calls, optionally filtered.
contactreads one person’s call history: calls exchanged with that number, both directions, across every line on the account.
- async list_contacts(*, number_id: str | None = None, limit: int | None = None, starting_after: str | None = None) dial_sdk.types.ContactsPage¶
dial contacts — one page of the numbers your lines have talked to.
Newest activity first. A contact is derived from message and call history, not stored: a number appears the moment it first exchanges anything with one of your lines, and there is no address book to add anyone to. Counts span every line on the account, so one person who reached two of your numbers is one contact — pass
number_idfor one line’s contacts and counts instead (an id not on your account yields an empty page). Groups are not contacts — seelist_groups().starting_afteris thelast_atof the last contact from the previous page; only contacts with strictly older activity are returned. Most accounts come back in one page —list_all_contacts()follows the cursor when they don’t.
- async list_groups() list[dial_sdk.types.Group]¶
dial group list — the group conversations your lines are in.
A group is a conversation that isn’t a phone number, so it carries an id of its own: pass it as
group_idtosend_message()orlist_messages(). Groups exist on WhatsApp lines and iMessage numbers.A group’s
namemay be None — it is read live from the line holding the conversation, so a line that cannot answer in time yields a null name rather than hiding the group. There is no join event: list again to see a group your line was just added to.
- async list_messages(*, number_id: str | None = None, direction: str | None = None, since: str | None = None, group_id: str | None = None, contact: str | None = None, search: str | None = None) list[dial_sdk.types.Message]¶
dial message list — list messages, optionally filtered.
Unfiltered this is the newest 100 messages ACROSS every conversation, so one thread’s history is capped by how busy the others are.
contactreads one person’s conversation as itself: messages exchanged with that number, both directions, across every line on the account. Group messages are never included — a group message is addressed to the group, and matching it against the participant who sent it would file their whole group into a private thread. Usegroup_idfor those.searchmatches the message body case-insensitively, anywhere in the text, over your whole history, and then returns the 100 most recent matches. Substring rather than full-text: no stemming and no word boundaries, sovoicematches “invoiced”.group_idrestricts the list to one group conversation and combines with the other filters. A group id that isn’t on your account yields an empty list rather than an error.
- async list_numbers() list[dial_sdk.types.PhoneNumber]¶
dial number list — list the account’s phone numbers.
- async lookup_number(number: str) dial_sdk.types.NumberLookup¶
dial lookup — what channels a phone number can receive on.
Works on ANY number in the world: it need not be one of yours, and you need not have messaged it before. Use it to pick a channel before spending a send.
A lookup Dial could not complete RAISES (502) rather than returning a negative verdict, so a
Falsealways means the number genuinely is not reachable on that channel — never that we could not find out. The two are worth treating differently: only one is worth retrying.supports["whatsapp"]is None when your account has no WhatsApp number of its own — a third value with its own single meaning, and never a failure either. SeeNumberLookup.
- 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 everymessage.received/call.status_changed/call.ended/call.transcribedevent on the account channel. The PubNub token is re-minted automatically before it expires. Every event shares one envelope; read values fromevent["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) orreaction(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 noto/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, calling_enabled: bool | None = None, forward_to: str | None = _UNSET, channel: str | None = None, name: str | None = None, first_name: str | None = _UNSET, last_name: str | None = _UNSET, avatar: str | Path | MediaItem | None = None, whatsapp_name: str | None = _UNSET, whatsapp_avatar: str | Path | MediaItem | None = None) dial_sdk.types.PhoneNumber¶
dial number set — update a number’s properties (any subset; at least one).
nickname=Noneornickname=""clears the nickname; omit the argument to leave it unchanged.inbound_voice_gender=Noneclears the inbound voice (reverts to the default, female); pass “male”/”female” to set it.inbound_language=Noneclears 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.channelwithnameand/oravatarsets one display identity on one or both channels:"imessage","whatsapp", or"both". Prefer"both"when the number should look the same everywhere — it is a single call, and the name and photo are validated against every targeted channel before any of them is written, so a value one channel rejects can’t leave the two profiles disagreeing. WhatsApp storesnameverbatim (1–25 chars); iMessage has no single-name field, so it is split on the first space —"Ana Lee"becomes first name"Ana", last name"Lee".channelcannot be combined with the per-channel arguments below.first_name/last_nameset the iMessage display identity on its own — the name shown beside the number’s messages in recipients’ Messages apps (max 30 chars;Noneor""clears; iMessage numbers only — rejected with 400 otherwise).avatarsets the identity photo: anhttp(s)URL string (downloaded server-side), a local imagePath, or aMediaItemof raw bytes (both uploaded as multipart). Withoutchannelthat is the iMessage photo: jpeg/png/gif/webp, max 5 MB. Withchannelit goes to the channel(s) named there, and must then also satisfy WhatsApp’s stricter rules when WhatsApp is targeted — square jpeg or png, 192×192 to 640×640, not resized, with no transparent pixels. Replace-only — a photo can be replaced but not removed.calling_enabledswitches calling on or off for the number, in both directions:Falsestops inbound calls from being connected (the caller is never answered) and makesmake_call()from this number raisecalling_disabled(409). Messaging is unaffected. It takes effect on the next call — a call already in progress is not ended. Omit the argument (or passNone) to leave it unchanged.forward_toforwards inbound calls to this phone number (E.164) instead of having the AI voice agent answer; the call ends if that phone is busy or doesn’t answer.forward_to=Nonestops forwarding; omit the argument to leave it unchanged. Rejected (400) when it isn’t valid E.164 or is the number’s own number. Has no effect while calling is switched off (calling_enabled=False).
- async start_typing(*, to_number: str, from_number: str, channel: str | None = None) 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_numberis 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, channel: str | None = None) None¶
dial typing stop — clear a typing indicator shown with start_typing.
Pass the same
channelstart_typingwas given.
- typing(*, to_number: str, from_number: str, channel: str | None = None) 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 everymessage.received/call.status_changed/call.ended/call.transcribed/number.status_changedevent 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.Group¶
A group conversation — a conversation that isn’t a phone number.
Pass
idasgroup_idwhen sending or listing messages.- channel: Literal['whatsapp', 'imessage']¶
- created_at: str¶
- id: str¶
- name: str | None¶
- 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¶
-
- body: str¶
- channel: str¶
- created_at: str¶
- delivery_error: str | None = None¶
- delivery_state: str | None = None¶
- direction: str¶
- from_: str¶
- group_id: str | None = None¶
- id: str¶
- media: list[MessageMediaItem] = []¶
- phone_number_id: str¶
- reaction: str | None = None¶
- read_at: str | None = None¶
- read_state: str | None = None¶
- reply_to_id: str | None = None¶
- status: str¶
- status_error: str | None = None¶
- to: str | None¶
- 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.MessageStatusChanged¶
Bases:
TypedDictdict() -> 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¶
- id: str¶
- object: Literal['event']¶
- type: Literal['message.status_changed']¶
- version: int¶
- class dial_sdk.MessageStatusChangedData¶
Bases:
TypedDictAn advance on one of an outbound message’s two independent axes.
Delivery and reads are separate facts — not every rail reports both — so the payload carries the current value of BOTH axes plus changed naming the one that just moved. Each event is therefore a complete snapshot.
Initialize self. See help(type(self)) for accurate signature.
- changed: str¶
- channel: str¶
- deliveryError: str | None¶
- deliveryState: str¶
- from_: str¶
- messageId: str¶
- phoneNumberId: str¶
- readState: str¶
- to: str¶
- class dial_sdk.NumberLookup¶
What a phone number can receive.
supportsdescribes the number that was asked about, NOT one of your own lines. It is a point-in-time answer rather than a property of the number: someone who changes device or turns a service off stops being reachable, so treat aTrueas a strong signal for picking a channel, not a promise that a later send is delivered.- classmethod from_api(d: dict) NumberLookup¶
- number: str¶
- supports: dict[str, bool]¶
- class dial_sdk.NumberStatusChanged¶
Bases:
TypedDictdict() -> 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: NumberStatusChangedData¶
- id: str¶
- object: Literal['event']¶
- type: Literal['number.status_changed']¶
- version: int¶
- class dial_sdk.NumberStatusChangedData¶
Bases:
TypedDictA change to one of a phone number’s capabilities.
Wait on this instead of polling a number until it is usable.
statusfolds the capabilities:unsettledwhile any is still working,readywhen all are ready,degradedwhen everything has finished trying and something failed.readyanddegradedare both terminal, so waiting on either always resolves —degradedexists because WhatsApp can be declined outright while the number still works for calls and messages.Payload version 2. Version 1 was flat (
setupStatus/setupErrorand acapabilitiesstring list) and had nowhere to put a WhatsApp failure or its cooldown.Initialize self. See help(type(self)) for accurate signature.
- capabilities: dict[str, CapabilityState]¶
- changed: list[str]¶
- number: str¶
- phoneNumberId: str¶
- previousStatus: str | None¶
- status: str¶
- class dial_sdk.PhoneNumber¶
- classmethod from_api(d: dict) PhoneNumber¶
- account_id: str¶
- avatar_url: str | None = None¶
- calling_enabled: bool¶
- capabilities: list[str]¶
- country: str¶
- created_at: str¶
- first_name: str | None = None¶
- forward_to: 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¶
- whatsapp: dict | None = None¶
- whatsapp_avatar_url: str | None = None¶
- whatsapp_name: str | None = None¶
- class dial_sdk.PurchaseNumberParams¶
- area_code: str | None = None¶
- calling_enabled: bool | None = None¶
- explicit_programmatic_consent: str¶
- 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¶
- group_id: str | None = None¶
- media: list[SendMessageMedia] = []¶
- to: str = ''¶
- class dial_sdk.TranscriptTurn¶
One uninterrupted stretch of speech by one party, placed in time.
Offsets count milliseconds from the start of the call’s audio, so the pause before a turn is its
start_msminus the previous turn’send_ms.The offsets are approximate: they come from per-word timings the voice runtime does not guarantee to be exact. Ample for spotting a long silence, which is what they are for; not a basis for splitting tenths of a second.
- classmethod from_api(d: dict) TranscriptTurn¶
- end_ms: int¶
- speaker: str¶
- start_ms: int¶
- text: str¶
- 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, channel: str | None = None)¶
A scoped typing indicator — see
DialClient.typing().- async send_message(**kwargs) dial_sdk.types.Message¶
Send a message in this conversation —
toand the from-number ref are prefilled.Accepts the same keyword fields as
SendMessageParamsminus 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__.The session’s
channelis prefilled too, so a session opened on WhatsApp does not show its indicator there and then send on the line’s default rail — a different conversation to the recipient. An explicitchannelkeyword still wins.
- 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>"), wheredatais the value the signature covers — thecall_idfor the self-hosted WebSocket protocol, or the raw request body for a webhook delivery. ReturnsFalse(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¶