WhatsApp Messaging
This guide shows how to send and receive WhatsApp messages with the AgentDuet
SDK. Voice stays on Call. Inbound WhatsApp arrives on SessionManager.
Outbound send stays on Session. One subscriber can speak and chat in the
same conversation.
WhatsApp must be configured on your connector. If it is not,
session.send_message() returns a SendMessageResult with
success=False and error_code CHANNEL_NOT_CONFIGURED.
What this channel provides
- Inbound WhatsApp at the connector via
@sm.on_incoming_message - Outbound WhatsApp via
session.send_message(SendWAMessage(...)) - Addressing with
Address.whatsapp(value) - Raw Meta webhook payloads so text, buttons, and media stay in application code
How the pieces fit
Inbound notifications carry addressing only plus the raw webhook payload.
Correlate by (subscriber, participant) yourself. Delivery is at-least-once:
dedup on IncomingMessage.id.
Prerequisites
- Python 3.12+ and
pip install agentduet==1.0.0 - AgentDuet API key and connector UUID from agentduet.com
- WhatsApp enabled on that connector
Receive and reply
Register @sm.on_incoming_message on the SessionManager (same process as
calls). Open a session for msg.subscriber, then send. The server infers the
recipient from the to field in the payload.
msg.payload is the raw WhatsApp webhook body. Its shape depends on the
message type (text, button, image, and so on). Inspect it before you reply.
To keep replies to the same customer on one session (and stay under the
participant cap), cache a session_id per (subscriber, participant) instead
of minting a new id on every message.
Start an outbound conversation
Inbound is not required to send. Open a session for your subscriber (the
business WhatsApp identity) and put the customer in the payload to. Replies
still arrive on @sm.on_incoming_message.
subscriber is your connector’s WhatsApp identity, not the customer’s.
Addressing
IncomingMessage.participant is already an Address on the WA network. Use
msg.participant.value as to when you reply.
Same session as a call
A Session can carry a call and messages for the same subscriber. Typical
pattern: handle the live call on Call, then send a WhatsApp follow-up on that
session after hangup (confirmation, summary, or a link).
Reuse the same session_id when you want one conversation. Use a new id when
you want a fresh thread.
Routing
By default the server delivers inbound messages. Turn delivery off (or back on) with trigger conditions:
Call and message routing are independent. You can take calls only, messages only, or both. The config you send is an absolute replace. See Trigger Conditions.
If you use VoiceAgent, inbound= rewrites trigger conditions on startup and
resets message-flow toggles to their defaults. Pass inbound=None to leave
the connector routing untouched.
Send results
send_message() returns a SendMessageResult. It does not raise for provider
or quota failures. Check result.success. Do not use if result:; unlike
CommandResult, SendMessageResult is not bool-like.
Unsupported message types passed to send_message() raise MessageError.
Server-side send failures stay on SendMessageResult.
