Messaging
Send and receive SMS/MMS messages through the RELAY client.
Sending Messages
Use client.send_message() to send an outbound SMS or MMS.
message = await client.send_message(
to_number="+15552222222",
from_number="+15551111111",
body="Hello from SignalWire!",
)
Wait for delivery
message = await client.send_message(
to_number="+15552222222",
from_number="+15551111111",
body="Hello!",
)
event = await message.wait() # blocks until delivered/failed
print(f"Final state: {message.state}")
if message.reason:
print(f"Reason: {message.reason}")
Fire and forget
message = await client.send_message(
to_number="+15552222222",
from_number="+15551111111",
body="Hello!",
)
# don't call message.wait() — continue immediately
Callback on completion
message = await client.send_message(
to_number="+15552222222",
from_number="+15551111111",
body="Hello!",
on_completed=lambda event: print(f"Delivery: {event.params.get('message_state')}"),
)
MMS (media messages)
message = await client.send_message(
to_number="+15552222222",
from_number="+15551111111",
body="Check this out!",
media=["https://example.com/image.jpg"],
)
All parameters
message = await client.send_message(
to_number="+15552222222", # required — E.164 format
from_number="+15551111111", # required — E.164 format
body="Message text", # required if no media
media=["https://..."], # required if no body
context="my_context", # context for state events (default: relay protocol)
tags=["vip", "support"], # optional tags for searching in UI
region="us", # optional origination region
on_completed=callback_fn, # optional completion callback
)
Receiving Messages
Register a handler with @client.on_message to receive inbound SMS/MMS.
from signalwire_agents.relay import RelayClient
client = RelayClient(
project="your-project-id",
token="your-api-token",
host="example.signalwire.com",
contexts=["default"],
)
@client.on_message
async def handle_message(message):
print(f"From: {message.from_number}")
print(f"To: {message.to_number}")
print(f"Body: {message.body}")
if message.media:
print(f"Media: {message.media}")
# Reply back
await client.send_message(
to_number=message.from_number,
from_number=message.to_number,
body=f"You said: {message.body}",
)
client.run()
Message Object
Properties
| Property | Type | Description |
|----------|------|-------------|
| message_id | str | Unique message identifier |
| context | str | Context the message belongs to |
| direction | str | inbound or outbound |
| from_number | str | Sender phone number (E.164) |
| to_number | str | Recipient phone number (E.164) |
| body | str | Text body of the message |
| media | list[str] | Media URLs (MMS) |
| segments | int | Number of message segments |
| state | str | Current message state |
| reason | str | Failure reason (on undelivered or failed) |
| tags | list[str] | Tags attached to the message |
| is_done | bool | True if message reached a terminal state |
| result | RelayEvent | Terminal event (or None if not done) |
Methods
| Method | Description |
|--------|-------------|
| await message.wait(timeout=None) | Block until terminal state. Returns the terminal RelayEvent. |
| message.on(handler) | Register a listener for state change events. |
Message States
Outbound messages progress through these states:
| State | Description |
|-------|-------------|
| queued | Message accepted and queued for sending |
| initiated | Sending has started |
| sent | Message sent to carrier |
| delivered | Message delivered to recipient (terminal) |
| undelivered | Delivery failed (terminal) — check reason |
| failed | Message failed to send (terminal) — check reason |
Inbound messages always arrive with state received.
Event Types
| Event | Description |
|-------|-------------|
| MessageReceiveEvent | Inbound message received |
| MessageStateEvent | Outbound message state change |
from signalwire_agents.relay import MessageReceiveEvent, MessageStateEvent
Combining Calls and Messages
The same RelayClient handles both calls and messages:
client = RelayClient(project="...", token="...", contexts=["default"])
@client.on_call
async def handle_call(call):
await call.answer()
await call.play([{"type": "tts", "params": {"text": "Hello!"}}])
await call.hangup()
@client.on_message
async def handle_message(message):
print(f"SMS from {message.from_number}: {message.body}")
client.run()