Skip to content

Phone Integrations

The Phone page connects communication providers, stores phone credentials, registers phone-number resources, and routes voice, SMS, or WhatsApp events into Voxos.

Provider Current capabilities
Twilio Credentials, phone number resource connection, inbound/outbound voice, inbound/outbound SMS, status webhooks, media streams, tool actions.
Telnyx Phone setup, dedicated voice and messaging routing, inbound/outbound voice, inbound/outbound SMS, media streams, tool actions.
WhatsApp linked device Link one existing Twilio or Telnyx number through the WhatsApp Business mobile app, receive live direct and group text, send durable agent replies, and send approved messages to saved contacts.

All integration secrets use the tenant-scoped user secret vault.

The Twilio form asks for:

  • Label.
  • Account SID.
  • API Key SID.
  • API Key Secret.
  • Auth Token.

After credentials are saved, Voxos can list candidate phone numbers. Select the phone number resources and capabilities to connect. Voxos creates webhook routes for voice, SMS, status, and media stream handling.

The Telnyx form asks for:

  • Label.
  • E.164 phone number.
  • API key.
  • Webhook public key.
  • Confirmation that Voxos can assign the number to Telnyx routing.
  • Optional transcript storage.

Voxos first checks that the selected number is active and exposes both voice and two-way SMS. It then creates dedicated Telnyx voice and messaging profiles, attaches the number when confirmed, and stores only the resulting safe provider identifiers and resource metadata. Setup fails with a capability-specific error instead of marking an unsupported number as ready.

Each connected Telnyx card has a read-only Verify action. It checks the number and its current assignments, the messaging profile and API v2 webhook, the Voice API application, the outbound voice profile and configured destination allowlists, the durable reply prerequisites, and LiveKit routing. The result reports inbound SMS, outbound SMS, inbound calls, outbound calls, agent reply processing, and LiveKit independently.

Repair requires an explicit browser and API confirmation. It updates the existing Voxos-owned number assignments, webhook URLs, API versions, and enabled states where they have drifted. It does not create replacement profiles on every check. Missing provider objects require reconnecting the integration instead of silently creating duplicates. Because the current public base URL is used during verification and repair, restart the API with the intended stable API_PUBLIC_BASE_URL before repairing an old tunnel URL.

These checks are available through tenant-scoped API routes:

  • GET /api/integrations/{integration_id}/telnyx/readiness
  • POST /api/integrations/{integration_id}/telnyx/repair
  • GET /api/integrations/{integration_id}/messages
  • GET /api/integrations/{integration_id}/calls
  • GET /api/integrations/{integration_id}/calls/latency

Readiness and activity responses use full remote phone numbers and bounded safe provider errors. They do not return credentials, route keys, message bodies, or raw Telnyx payloads.

Voxos links a WhatsApp Business mobile account as a companion device so the selected agent can receive and reply to direct and group text messages sent to a connected Twilio or Telnyx number. The mobile app remains the primary account and may occasionally require relinking.

Twilio or Telnyx number rental, SMS or voice verification, mobile data, and other carrier costs can apply. WhatsApp decides whether a number is eligible for mobile-app registration and may change linked-device behavior or restrict an account.

The current release supports one linked WhatsApp number per Voxos user. A single deployment remains multi-tenant: each user owns a separate encrypted session, resource, conversation, and outbox.

The number shown to customers is the number registered in WhatsApp Business, so select the dedicated Voxos business number rather than a team member’s personal WhatsApp number. That exact E.164 number must already be a connected Twilio or Telnyx phone resource in the same Voxos account.

Phone-number shape and messaging reach are separate properties. A number can look like a UK mobile and support domestic SMS while still rejecting an OTP sent through an international application-to-person route.

Number type Registration guidance
Telnyx UK mobile Supports ordinary SMS and voice, but Telnyx country coverage currently reports international_sms: false. Use Verify with a call for WhatsApp registration.
Twilio UK mobile Twilio documents domestic and international messaging support. Confirm the actual number’s capabilities and keep voice available as a fallback.
UK local, national, or landline Do not assume SMS support. WhatsApp Business can use a voice call when the selected provider resource can receive the automated call. Voxos currently automates that call flow for Telnyx resources.
Toll-free, premium, shared-cost, VoIP, or other programmable DID Eligibility and OTP delivery vary by carrier and WhatsApp. Test registration before assigning the number to customers. A connected Voxos resource is not, by itself, proof of WhatsApp eligibility.

Telnyx exposes sms and international_sms as distinct inventory features; messaging-profile assignment cannot convert one into the other. Twilio also distinguishes domestic-only from globally SMS-enabled numbers. Check the current Telnyx number-search coverage or Twilio SMS number capabilities before buying or replacing a number.

An international sender does not normally make a UK subscriber pay to receive an ordinary SMS while in the UK; carrier and roaming terms still apply. For example, O2 states that receiving a call or message in the UK from overseas has no additional charge in its international calling guidance. The practical risk for programmable numbers is nondelivery, because the number or upstream carrier may not accept the sender’s route.

Before opening the WhatsApp setup panel:

  1. Connect the exact E.164 phone number to Voxos through Twilio or Telnyx. The source integration and phone-number resource must both be connected.
  2. Install and open WhatsApp Business on a phone. If it offers the phone’s existing personal number, choose Use a different number and enter the dedicated Voxos number.
  3. Open Phone at /integrations, find WhatsApp linked device, select the same Twilio or Telnyx number, shown in full, and arm the capture method before requesting the code in WhatsApp Business:
    • For a Telnyx number, choose Verify with a call, then choose Call me in WhatsApp Business and keep the Voxos page open. WhatsApp may first ask the receiver to press a digit. The requested digit varies between calls; Voxos reads the prompt, sends that digit, captures the spoken verification code, and displays it in the panel.
    • Choose Verify with SMS only when the selected number can receive the route WhatsApp uses. Voxos captures a numeric verification-code SMS for the exact selected resource during the ten-minute window.
  4. Enter the code shown by Voxos into WhatsApp Business. Code length and spoken grouping can vary, so use the complete code displayed rather than assuming a fixed format. Wait for the app to confirm registration, then select I entered this code in Voxos.
  5. Choose QR code or Pairing code in Voxos. In WhatsApp Business, open Linked devices, choose Link a device, then scan the QR or use Link with phone number instead and enter the temporary code.
  6. Keep the page open until it shows Number verified and connected. The panel records separate verified and connected timestamps.

If the number was already registered, choose It is already registered and start directly at the linked-device step.

The panel polls the authoritative backend state and changes to Connected with verified and connected timestamps when the link is accepted. Temporary registration codes and linked-device challenges are returned with Cache-Control: no-store and are kept only in React memory. Linked-device challenges expire and can be regenerated.

The Twilio or Telnyx integration supplies the expected number and default agent. Its authenticated SMS webhook can deliver the one-time registration code to the short setup window. For Telnyx voice verification, the signed voice webhook answers the active setup call and starts inbound transcription without routing the call to the normal voice agent. After linking, WhatsApp traffic uses the linked-device socket. Provider webhooks still need a public API_PUBLIC_BASE_URL.

Do not repeatedly request new WhatsApp codes. WhatsApp applies retry cooldowns, which can prevent both SMS and call attempts for an hour or longer.

  1. Leave the number unchanged and check whether the provider received an SMS or call at the time of the request.
  2. If the SMS never reached the provider, Voxos cannot recover it. The usual cause is number capability, international A2P reach, or upstream filtering; use Verify with a call when available.
  3. For a voice retry, cancel an expired Voxos attempt, choose Verify with a call again, wait until the panel says it is waiting, and only then request Call me in WhatsApp Business. Each call must have an active capture window; caller ID and the requested DTMF digit can change.
  4. If the provider received the event but Voxos did not show a code, verify the public webhook route and provider assignment. For Telnyx, use Verify on the phone integration before Repair; Repair can correct routing but cannot add an unsupported international-SMS capability.
  5. If WhatsApp shows a cooldown, wait for its timer to expire before arming one fresh attempt. Requesting more codes during the cooldown does not help.

The SMS and call routes are alternatives for the same initial mobile registration. After WhatsApp Business accepts either code, the QR or pairing code is a separate linked-device step.

Capturing a mobile-registration SMS or spoken code proves only that the tenant’s provider resource received a code. It does not authorize the number or mark the WhatsApp integration connected.

The backend accepts SMS candidates only after the normal Twilio or Telnyx signature, provider-account, route, and exact resource-number checks. A Telnyx verification call must arrive through the signed voice route for the same tenant integration and resource while Verify with a call is armed. Voxos does not request a LiveKit worker for that call and does not create agent or call-transcript rows. The temporary record, SMS envelope, spoken transcript fragments, and parsed voice candidate are tenant-scoped and encrypted in the user secret vault. A rejected SMS candidate is replayed once through the provider’s normal SMS path; a rejected voice candidate is deleted. Outside the ten-minute setup window, normal SMS and voice routing is unchanged.

After WhatsApp establishes the companion session, the bridge reports its phone-number (PN) identity. The backend normalizes that authoritative PN and compares it with the selected tenant-owned Twilio or Telnyx resource. Only a PN-bearing identity proof for the exact number can enable the integration, resource, inbound processing, and outbound replies. A linked-device ID (LID) alone is insufficient.

If the numbers differ, or WhatsApp cannot provide a PN identity, Voxos rejects the validation, removes the attempted linked device, clears its local auth state after confirmed logout, and shows that relinking is required. The frontend never sends a user id, identity result, or “validated” flag as authority.

make worker-whatsapp runs the Python supervisor and builds/starts its private Node.js bridge. The complete Compose equivalent is:

Terminal window
make up

This is a shared multi-session service, not one operating-system process per user. One worker owns one private stdio bridge and multiplexes up to WHATSAPP_MAX_SESSIONS_PER_WORKER live linked-device sockets, 25 by default. Database leases and generations fence duplicate workers and stale events, so multiple worker replicas can share the same database. A connected WhatsApp number still needs a live socket; durable credentials allow another worker to restore that socket after a restart without scanning a new QR unless WhatsApp has revoked the linked device.

Baileys credentials and Signal keys are never stored in a filesystem auth directory. Voxos generates a per-integration data key, seals it in the tenant’s user secret vault, and AES-GCM encrypts each auth value and temporary challenge in Postgres with tenant, integration, key identity, and revision bound as authenticated context. The bridge child has no HTTP listener, database credentials, vault credentials, or filesystem session store. Plaintext auth is limited to the owning worker and bridge memory while needed.

Mobile-registration SMS envelopes and voice-verification data use a separate vault purpose and are deleted when setup completes or is cancelled. The WhatsApp worker has no cross-tenant access to the registration table or vault entry.

The integration accepts live direct and group text notifications. It ignores broadcasts, status/newsletter traffic, media, history message appends, and messages sent by the linked account. History synchronisation may still enrich identity mappings; it does not import old chat messages into agent context. An approved agent can send a proactive WhatsApp message to a saved contact through a connected, validated linked session. The action requires search_contacts first, accepts the returned contact ID rather than a free-form number, and queues the message for durable delivery by whatsapp-worker.

For each direct message, Voxos reads remoteJid and remoteJidAlt. For a group message it reads participant and participantAlt. Each value is classified with Baileys’ PN and LID predicates instead of assuming that an identifier’s digits are a phone number. A PN JID is authoritative phone identity. An LID is authoritative WhatsApp identity but is not itself a phone number.

When a message contains only an LID, the bridge checks contact records, current group participant metadata, Baileys’ Signal LID mapping, and mappings learned from live mapping events or history synchronisation. Signal mappings remain in the integration’s encrypted remote-auth state and survive worker restarts. A verified PN/LID pair is stored in one tenant- and integration-scoped identity record. When the PN produces an exact E.164 match to a saved contact, the inbound message and durable agent run retain that contact ID and display name. WhatsApp display names are labels only and never establish contact identity or access.

If no PN mapping exists, Voxos retains the participant using the LID without deriving a number from its digits or guessing a saved contact. The message is recorded, but its reply is marked suppressed because the sender cannot be matched to a saved contact. A later authoritative PN/LID observation enriches the same identity and can link it through exact E.164 contact matching for new messages; it does not replay an older suppressed message. Conflicting aliases are marked as a conflict and remain ineligible for automated replies.

Direct conversations are keyed by the sender’s stable WhatsApp identity. Group conversations are keyed by the group JID, so recent live messages from different participants share the same bounded agent context with a sender label on each inbound turn. Replies are sent back to the originating direct chat or group. Saved-contact admin permissions are not applied to group messages because tool results would be visible to other group participants.

An inbound message is acknowledged to the bridge only after Voxos has durably claimed the provider message ID, stored the tenant-owned message, resolved its per-peer conversation, and recorded a reply disposition. A saved contact queues one communication_reply run. An unknown sender receives one idempotent suppressed reply record with no run, transcript, outbox item, or automated response. Duplicate notifications reuse the original disposition. Admitted conversations use the whatsapp:v1 mobile-response profile and the same low-risk inbound tool-policy boundary as SMS.

make worker-runs generates the response and writes one durable WhatsApp outbox item. make worker-whatsapp sends queued items only while the exact tenant session remains validated and connected. Outbox state advances through queued, submitting, and then sent, failed, or ambiguous. A returned WhatsApp message ID marks the reply provider_accepted; it is not proof that a recipient handset displayed the message because this transport does not yet persist delivery receipts.

Retryable failures before a conclusive submission can return an item to queued. A lost or uncertain submission result becomes ambiguous and is not automatically resent. Worker leases and generations prevent a stale process from completing another worker’s item. On disconnect, queued items fail and an item already submitting becomes ambiguous.

Use Relink WhatsApp when the companion device is revoked or the panel shows needs_relink. Relink first requests confirmed remote logout, deletes the old encrypted auth state, advances the worker generation, and then presents a fresh QR or pairing code for the same source number.

Disconnect WhatsApp immediately disables new delivery and shows disconnecting. The worker confirms remote logout before it deletes session keys and challenges, removes the vault data key, revokes the credential, and removes the integration. If logout is uncertain, Voxos retains the encrypted state and retries instead of claiming that the device was removed. To use a different source number, disconnect the current integration completely and start a new link.

Authenticated setup routes are:

  • GET /api/integrations/whatsapp/options
  • POST /api/integrations/whatsapp/link
  • GET /api/integrations/whatsapp/{integration_id}/status
  • GET /api/integrations/whatsapp/{integration_id}/challenge
  • POST /api/integrations/whatsapp/{integration_id}/challenge
  • POST /api/integrations/whatsapp/{integration_id}/relink
  • DELETE /api/integrations/{integration_id}

WhatsApp can change companion-device behavior, revoke sessions, rate-limit accounts, or take enforcement action. There is no production compatibility or delivery SLA. Use the integration only for numbers, recipients, and content you are authorized to process, follow WhatsApp and carrier terms and opt-in rules, and complete a privacy, data-processing, acceptable-use, and dependency-license review before production rollout.

A valid signed message.received webhook is matched to the configured phone resource, durably claimed by Telnyx message ID, and stored before Voxos returns success. An exact saved-contact match produces one reply record and at most one queued communication-reply run. An unknown number is retained in activity with a suppressed reply and receives no generated or outbound message. Retries reuse the original disposition, so adding the number as a contact later affects only new messages. Voxos binds each message to a reusable SMS conversation isolated by tenant, provider, integration, phone resource, remote sender, and agent. The active session rolls over after 24 hours of inactivity; different senders and web-chat or voice sessions do not share its transcript.

The model produces response text, but it does not send the reply or select its destination. The communication adapter derives the provider, source resource, and verified inbound sender from tenant-scoped records, validates the complete response, and submits it. The proactive send-SMS tool remains available for approved agent actions, but it is not part of an inbound reply run.

Inbound SMS uses the versioned sms:v1 response profile: plain text, direct, normally one or two short sentences, with one segment preferred and two segments the ordinary limit. Voxos calculates GSM-7 septets (including the extension table) or UTF-16 code units rather than counting Python characters. One GSM-7 segment holds 160 units and multipart segments hold 153 each; Unicode uses 70 and 67 UTF-16 units. Meaningful Unicode and the user’s language are preserved. If a generated answer is too long, Voxos performs one tool-free semantic condensation pass. If it is still outside the supported envelope, it sends a complete short fallback instead of slicing or silently dropping it.

The inbound peer is not an authenticated Voxos user. Its run policy is the intersection of the agent’s grants and a low-risk channel allowlist. Current time and product-doc tools may be used when granted; an explicitly granted web_search_exa tool may also qualify only through the tenant’s enabled, trusted, connected server whose persisted Exa preset identity and URL match the catalog.

An exact E.164 sender match to a saved contact marked Admin may additionally receive search_contacts, but only when the selected agent already has that internal-tool grant. The tool returns contact IDs, display names, and full phone numbers. Unknown and non-admin senders cannot access contacts. Even admin SMS senders cannot receive create_task_plan, integration delivery, credentials, private memory, other mutating tools, or arbitrary outbound actions through this channel. create_task_plan records planning data; it does not schedule or send a message. The communications adapter, not an LLM tool call, delivers the conversational reply to the verified inbound route.

If no allowed tool can establish a current fact, the SMS profile requires a short honest limitation or focused follow-up question. The Admin flag relies on caller ID and is not strong authentication, so higher-impact actions require a dedicated confirmed and preferably step-up-authenticated workflow.

agent-run-worker is part of the default root Compose stack. Host-based local development must run make worker-runs in a separate terminal. A stored inbound message with a queued run cannot produce an automated reply while that worker or the user’s active text provider is unavailable.

Reply state is separate from carrier message state. Activity can show pending, generated, dispatching, provider_accepted, delivered, failed, suppressed, or ambiguous, together with the response profile, estimated encoding, and segment count. A run succeeds only after provider acceptance. Delivery callbacks remain authoritative for final delivered or failed state. If a process loses the provider result after submission begins, the reply becomes ambiguous and automatic resend is blocked.

Outbound Telnyx messages use a per-message status callback and disable messaging profile callback precedence for that send, leaving the profile webhook for inbound messages. Final state comes from each recipient in payload.to[].status: delivered is delivered, sending_failed and delivery_failed are failed, and delivery_unconfirmed remains a distinct terminal non-delivered state. Only safe error codes and titles are retained for ordinary activity views.

Sender registration and allowed destinations remain Telnyx/account requirements. US long-code and toll-free traffic can require 10DLC registration or toll-free verification; verify the applicable number type, balance, permissions, and compliance state in Telnyx before testing.

Provider webhooks need a public HTTPS API base URL. For local development, use make ngrok, then set API_PUBLIC_BASE_URL to that https:// URL before configuring provider callbacks.

Webhook routes are under /api/integrations/webhooks/.... Media stream routes use websocket paths under /api/integrations/streams/....

As part of the same tenant transaction that creates a verified voice call session, Voxos records demand for that call’s saved LiveKit project. It commits the demand and waits for registration before returning Twilio streaming TwiML, answering an inbound Telnyx call, or preparing an outbound call. Twilio and Telnyx identifiers are matched only inside the verified webhook tenant scope; they are not user authority.

For a new goal-directed outbound call, Voxos persists the bounded caller-safe objective and trusted initiating run reference before provider submission. It then explicitly dispatches the named LiveKit agent to the reserved room. The worker loads the objective inside the exact tenant, agent, carrier, and runtime session scope, renders the task prompt, starts the selected realtime model with input disabled, and records agent_ready_at. Only after that readiness signal does the API ask Twilio or Telnyx to dial.

The outbound carrier bridge uses a stable participant identity derived from the server-created call-session ID. Its token intentionally omits agent_name, because the agent was already dispatched; including it would create a duplicate job. Inbound calls retain the dispatch-bearing participant token path.

Outbound Twilio callbacks carry a server-created call-session correlation ID. After Twilio signature verification, Voxos binds the provider Call SID to that original reservation so an early callback cannot replace the selected agent, runtime session, room, or worker demand.

Telnyx uses the same server-created reservation through base64 client_state and a stable command ID for that reservation. Verified non-init events reuse the exact tenant, integration, and resource-scoped call session instead of creating or refreshing another worker demand. Provider completion releases the live demand in the same tenant transaction; failed or rejected calls cancel it with a provider-specific failure code.

The outbound opening is generated exactly once only after both verified carrier answer and LiveKit media readiness. Twilio normally supplies answer first and media second. Telnyx can establish its stream before originate, so streaming.started records media readiness without setting answered_at; call.answered or call.bridged supplies the answer signal. Input is enabled as soon as the objective-led opening is scheduled, preserving normal barge-in. An interruption does not remove the objective from the system prompt. The agent introduces itself only in its first spoken turn, then continues without repeating its name unless the callee asks or appears confused.

Pre-dial agent readiness, answer/media wait, opening generation/playout, and carrier ringing are bounded. Current carrier adapters request a 30-second ring timeout. Terminal provider states release the demand and close the prewarmed job. Prewarming can increase realtime-provider session time while the phone rings; measure that cost against the first-speech improvement.

Concurrent calls with identical LiveKit settings share the active project worker. Different projects and registration failures are isolated in separate child processes, and idle project workers drain automatically.

Phone session policy comes only from the activated, RLS-protected demand row. For telnyx and twilio sources, ordinary speech inactivity does not shut down the LiveKit agent: the call remains active until the verified provider media participant disconnects. Browser, job, and room metadata cannot opt a session into this behavior. The worker does not issue carrier hangup commands because provider credentials and call-control authority remain in the tenant-scoped communications layer.

Telnyx voice uses bidirectional RTP media with mono PCMU at 8 kHz. The Voxos bridge publishes the caller into LiveKit and sends agent audio back to Telnyx in explicit 20 millisecond frames (160 encoded bytes). A successful downlink logs Started Telnyx PCMU media downlink once without audio content or tenant credentials. The first valid caller frame likewise logs Started Telnyx PCMU media uplink once with only call/runtime/stream correlation, codec, sample rate, and encoded byte count. Telnyx websocket error frames are sanitized to their numeric code and bounded title, persisted as media_stream_error, and fail only the owning call with media_stream_protocol_error. Unexpected LiveKit-to-Telnyx send or conversion failures use media_downlink_failed.

Both Telnyx and Twilio carrier bridges classify their local caller-audio publication as LiveKit SOURCE_MICROPHONE. This classification is required by LiveKit Agents RoomIO; it is separate from decoding a provider payload and capturing a PCM frame. For a healthy Telnyx call, safe logs should show demand activation, one first-uplink transition, one first-downlink transition, a participant disconnect after provider stop/hangup, and one demand completion.

Outbound submission is not yet durably idempotent across a fresh API or agent tool retry. If a provider response is lost after submission, do not automatically place the call again; a new request creates a new reservation and can produce a duplicate call.

Before a Telnyx outbound call reserves a voice worker, Voxos performs the LiveKit preflight and a read-only provider-routing check. The number must still be assigned to its active Voice API application and enabled outbound voice profile, and the profile must have configured destination permissions. Provider dial failure is stored as a safe call-session failure code and cancels the reserved demand.

The tenant-scoped latency endpoint reports sample counts plus p50/p95 milliseconds for worker-registration-to-agent-ready, agent-ready-to-provider-dial, provider-dial-to-answer, provider-dial-to-media-ready, answer-to-first-speech, and opening-scheduled-to-playout-complete. It never returns objectives, destinations, transcripts, provider payloads, or signed routes. Establish a real-carrier baseline before selecting an SLO.

Contacts are tenant-owned people with a display name, one E.164 phone number, optional email and notes, and an is_admin caller-access flag. Phone numbers are unique per user.

After a provider webhook has established the tenant, Voxos matches the remote party by exact E.164 number: the from number for inbound calls and the to number for outbound calls. The call session stores the matched contact ID, display name, and admin decision once. Editing the contact later does not change an active call. Unknown, withheld, malformed, or wrong-tenant numbers remain limited.

An admin contact receives the selected agent’s normal approved capabilities for the voice channel. This does not bypass agent grants, MCP trust, server allowlists, or provider authorization. A limited contact receives at most web_search_exa from the tenant’s verified Exa preset server: its persisted server ID must use a canonical URL from the current exa preset, retain preset_id=exa metadata, be enabled, trusted, and connected, explicitly allow the tool, and already appear in the selected agent’s base policy. A custom MCP server or gateway with a same-named tool is not accepted. Voxos does not install, trust, connect, allow, or grant search automatically.

Caller ID is not strong authentication. It can be spoofed, forwarded, or reassigned, so only use the admin flag for trusted callers and avoid granting high-impact tools without a future step-up check.

Approved text and durable agents use search_contacts before asking whether a named person is saved. The result reports not_found, unique, or ambiguous: one match can proceed directly, multiple matches require a choice, and no match requires a number or contact-creation clarification. The agent then passes the tenant-owned contact ID and complete caller-safe objective to the phone integration’s place-call tool. Voxos resolves the saved number inside the run’s tenant before calling Twilio or Telnyx. Authenticated provider API actions still accept an E.164 destination directly and require an objective.

Phone tools are integration tools. They require connected integrations, per-agent grants, and tool authorization. Agent tool calls must not supply user ids, credentials, URLs, headers, or other authority-bearing fields. Agent place-call tools accept saved contact IDs instead of model-supplied phone numbers and require a bounded objective. Connecting a call-capable phone resource grants the default agent the low-risk search_contacts prerequisite only when no contact-search decision exists. Existing allowed or denied decisions are preserved. If contact search is denied, the dependent place-call tool is omitted from that agent’s advertised toolset.

Use only an approved destination and record timestamps plus safe provider IDs; never paste message content, full numbers, route keys, or credentials into test notes.

Direction Expected evidence
Inbound SMS Telnyx webhook succeeds; one message, per-peer conversation, reply record, and queued run appear; the worker produces exactly one provider-accepted reply or an explicit failure/ambiguous state.
Outbound SMS The API returns a Telnyx message ID; activity advances through accepted/sent to delivered, failed, or delivery unconfirmed.
Inbound call One call session and worker demand appear; ringing/answer/media/hangup events correlate and demand is released.
Outbound call A saved tenant contact and objective produce one reservation; agent/model readiness precedes provider dial; no opening occurs until answer plus media; exactly one objective-led opening and safe latency timestamps appear; provider failure also releases demand.

The code paths and automated provider doubles are verified in the repository. Do not treat that as proof of carrier delivery, registration, audio quality, or destination permission until this matrix has been run with real approved endpoints.