> Fetch clean Markdown by appending `.md` to any page URL under https://signalwire.com/docs or requesting it with the HTTP header `Accept: text/markdown`. The root index at https://signalwire.com/docs/llms.txt lists the available documentation indexes. # Message > SMS/MMS message tracking and state management. [relayclient-send-message]: /docs/server-sdks/reference/python/relay/client/send-message [message-constants]: /docs/server-sdks/reference/python/relay/constants [relayevent]: /docs/server-sdks/reference/python/relay/events [message-on]: /docs/server-sdks/reference/python/relay/message/on [events]: /docs/server-sdks/reference/python/relay/events#messaging-events [on]: /docs/server-sdks/reference/python/relay/message/on [wait]: /docs/server-sdks/reference/python/relay/message/wait The `Message` class represents a single SMS/MMS message in the Relay messaging namespace. It tracks the lifecycle of a sent or received message through state events. Outbound messages progress through `queued`, `initiated`, `sent`, and then reach a terminal state (`delivered`, `undelivered`, or `failed`). Inbound messages arrive fully formed with state `received`. Obtain a `Message` instance from [`RelayClient.send_message()`][relayclient-send-message] or from the `@client.on_message` handler for inbound messages. ```python import asyncio from signalwire.relay import RelayClient client = RelayClient( project="your-project-id", token="your-api-token", contexts=["default"], ) async def send_sms(): async with client: message = await client.send_message( to_number="+15551234567", from_number="+15559876543", body="Hello from SignalWire!", ) # Wait for delivery confirmation result = await message.wait(timeout=30) print(f"Final state: {message.state}") asyncio.run(send_sms()) ``` ## **Properties** **`message_id`** `str` Unique identifier for this message, assigned by SignalWire. --- **`context`** `str` The messaging context this message belongs to. --- **`direction`** `str` Message direction. Valid values: * `"inbound"` -- incoming message * `"outbound"` -- outgoing message --- **`from_number`** `str` Sender phone number in E.164 format. --- **`to_number`** `str` Recipient phone number in E.164 format. --- **`body`** `str` Text content of the message. --- **`media`** `list[str]` List of media URLs for MMS messages. Empty list for SMS-only messages. --- **`segments`** `int` Number of SMS segments required for this message. --- **`state`** `str` Current message state. See [`Message Constants`][message-constants] for valid values. * `"queued"` -- message has been accepted and is waiting to be processed * `"initiated"` -- message processing has started * `"sent"` -- message has been dispatched to the carrier * `"delivered"` -- message was successfully delivered to the recipient * `"undelivered"` -- carrier was unable to deliver the message * `"failed"` -- message could not be sent * `"received"` -- inbound message received from the network --- **`reason`** `str` Failure reason when the message reaches an error state. Empty string if no failure has occurred. --- **`tags`** `list[str]` Optional tags associated with this message. --- **`is_done`** `bool` `True` if the message has reached a terminal state (`delivered`, `undelivered`, or `failed`). Read-only property. --- **`result`** `RelayEvent | None` — default: None The terminal [`RelayEvent`][relayevent] that resolved this message, or `None` if the message has not yet completed. --- ## **Events** Events are emitted during the lifecycle of a message. Register handlers using [`message.on()`][message-on] to react to state changes on outbound messages. See the [Events][events] reference for the full list of messaging events, their parameters, and typed event classes. --- ## **Methods** #### [on](/docs/server-sdks/reference/python/relay/message/on) Register an event listener for state changes on this message. #### [wait](/docs/server-sdks/reference/python/relay/message/wait) Block until the message reaches a terminal state. ## **Examples** ### Listening for state changes ```python import asyncio from signalwire.relay import RelayClient client = RelayClient( project="your-project-id", token="your-api-token", contexts=["default"], ) async def track_state(): async with client: message = await client.send_message( to_number="+15551234567", from_number="+15559876543", body="Order confirmed", ) def on_state_change(event): print(f"Message {message.message_id} -> {message.state}") message.on(on_state_change) await message.wait() asyncio.run(track_state()) ``` ### Waiting for delivery with timeout ```python import asyncio from signalwire.relay import RelayClient client = RelayClient( project="your-project-id", token="your-api-token", contexts=["default"], ) async def send_with_timeout(): async with client: message = await client.send_message( to_number="+15551234567", from_number="+15559876543", body="Your verification code is 123456", ) try: event = await message.wait(timeout=30) if message.state == "delivered": print("Message delivered successfully") else: print(f"Message failed: {message.reason}") except asyncio.TimeoutError: print("Timed out waiting for delivery confirmation") asyncio.run(send_with_timeout()) ``` > SMS/MMS message tracking and state management. ## Docs - [on](https://signalwire.com/docs/server-sdks/reference/python/relay/message/on.md): Register an event listener for state changes on this message. - [wait](https://signalwire.com/docs/server-sdks/reference/python/relay/message/wait.md): Block until the message reaches a terminal state.