> This page is for Server SDKs.

> 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.

# Events

> Typed event classes for all Relay events.

[call-on]: /docs/server-sdks/reference/python/relay/call/on

[message-on]: /docs/server-sdks/reference/python/relay/message

[constants]: /docs/server-sdks/reference/python/relay/constants

[playaction]: /docs/server-sdks/reference/python/relay/actions

[collect]: /docs/server-sdks/reference/python/relay/call/collect

[playandcollect]: /docs/server-sdks/reference/python/relay/call/play-and-collect

[sendfax]: /docs/server-sdks/reference/python/relay/call/send-fax

[receivefax]: /docs/server-sdks/reference/python/relay/call/receive-fax

[hold]: /docs/server-sdks/reference/python/relay/call/hold

[unhold]: /docs/server-sdks/reference/python/relay/call/unhold

[queueenter]: /docs/server-sdks/reference/python/relay/call/queue-enter

[queueleave]: /docs/server-sdks/reference/python/relay/call/queue-leave

Relay events are delivered as typed dataclass instances that wrap the raw JSON-RPC
event payloads from the SignalWire WebSocket connection. When you register a handler
with [`Call.on()`][call-on] or
[`Message.on()`][message-on], the handler
automatically receives a typed event object with named properties for each field.

All typed event classes inherit from [`RelayEvent`](#relayevent), which provides
access to the raw `params` dictionary alongside the typed properties. You can also
use the [`parse_event()`](#parse_event) helper to manually parse raw event payloads.

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import CallStateEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def on_call(call):
    def handle_state(event: CallStateEvent):
        print(f"Call state: {event.call_state}")

    call.on("calling.call.state", handle_state)
    await call.answer()

client.run()
```

## RelayEvent

Base event class. All other event classes inherit from this. Every handler receives
at minimum a `RelayEvent` instance, which provides access to the raw event data.

**`event_type`** `str`

The event type string (e.g., `"calling.call.state"`, `"messaging.receive"`).
See [`Constants`][constants] for the full list.

---

**`params`** `dict[str, Any]`

The raw parameters dictionary from the event payload. Contains all event-specific
fields, including those not surfaced as typed attributes on subclasses.

---

**`call_id`** `str`

The call identifier associated with this event, if applicable.

---

**`timestamp`** `float` — default: 0.0

Server timestamp of the event.

---

## **Methods**

### from_payload

**from_payload**(`payload`) -> `RelayEvent`

Class method. Parse a raw event dict into a `RelayEvent` instance.

#### Parameters

**`payload`** `dict[str, Any]` — required

Raw event payload dictionary.

---

**`payload.event_type`** `str` — required

The event type string (e.g., `"calling.call.state"`).

---

**`payload.params`** `dict[str, Any]` — required

Event-specific parameters.

---

#### Returns

`RelayEvent` -- A new event instance with typed properties.

#### Example

```python
from signalwire.relay.event import RelayEvent

event = RelayEvent.from_payload({
    "event_type": "calling.call.state",
    "params": {"call_state": "answered"},
})
print(event.event_type)  # "calling.call.state"
```

---

### parse_event

**parse_event**(`payload`) -> `RelayEvent`

Module-level helper function. Parses a raw event dict and returns the appropriate
typed event subclass based on the `event_type` field. Falls back to `RelayEvent`
for unrecognized event types.

#### Parameters

**`payload`** `dict[str, Any]` — required

Raw event payload dictionary.

---

**`payload.event_type`** `str` — required

The event type string (e.g., `"calling.call.state"`, `"calling.call.play"`).
Determines which typed subclass is returned.

---

**`payload.params`** `dict[str, Any]` — required

Event-specific parameters. Contents vary by event type.

---

#### Returns

[`RelayEvent`](#relayevent) -- The appropriate typed subclass based on the
`event_type` field, or a base `RelayEvent` for unrecognized types.

#### Example

```python
from signalwire.relay.event import parse_event

event = parse_event({
    "event_type": "calling.call.play",
    "params": {"control_id": "abc-123", "state": "playing"},
})
# Returns a PlayEvent instance
print(event.state)  # "playing"
```

---

## Calling Events

### calling.call.state

Emitted when the call state changes through its lifecycle: `created` ->
`ringing` -> `answered` -> `ending` -> `ended`.

#### CallStateEvent

Handlers for this event receive a `CallStateEvent` with the following properties:

**`call_state`** `str`

The new call state.

* `"created"` -- call object has been initialized
* `"ringing"` -- call is ringing at the destination
* `"answered"` -- call has been answered and is active
* `"ending"` -- hangup is in progress
* `"ended"` -- call has been fully terminated

---

**`end_reason`** `str`

Reason the call ended. Only present when `call_state` is `"ended"`.

* `"hangup"` -- normal disconnect by a party on the call
* `"cancel"` -- call was cancelled before being answered
* `"busy"` -- destination signaled busy
* `"noAnswer"` -- call rang but was not answered within the timeout
* `"decline"` -- destination actively declined the call
* `"error"` -- an error occurred during call processing
* `"abandoned"` -- caller hung up before the call was answered
* `"max_duration"` -- call reached the maximum allowed duration
* `"not_found"` -- destination could not be found or routed

---

**`direction`** `str`

Call direction.

* `"inbound"` -- incoming call
* `"outbound"` -- outgoing call

---

**`device`** `dict[str, Any]`

Device information for the call endpoint.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import CallStateEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_state(event: CallStateEvent):
        print(f"State: {event.call_state}, reason: {event.end_reason}")

    call.on("calling.call.state", handle_state)
    await call.answer()

client.run()
```

---

### calling.call.receive

Emitted when an inbound call is received on a subscribed context. This event is
dispatched at the client level via the `@client.on_call` decorator rather than
through `call.on()`.

#### CallReceiveEvent

Handlers for this event receive a `CallReceiveEvent` with the following properties:

**`call_state`** `str`

Initial call state (typically `"ringing"`).

---

**`direction`** `str`

Always `"inbound"` for receive events.

---

**`device`** `dict[str, Any]`

Device information for the caller.

---

**`node_id`** `str`

Relay node handling this call.

---

**`project_id`** `str`

SignalWire project ID.

---

**`context`** `str`

The context the call was received on.

---

**`segment_id`** `str`

Call segment identifier.

---

**`tag`** `str`

Correlation tag for the call.

---

### calling.call.play

Emitted when playback state changes on a play operation.

#### PlayEvent

Handlers for this event receive a `PlayEvent` with the following properties:

**`control_id`** `str`

The control ID of the play operation. Matches the `control_id` on the
[`PlayAction`][playaction].

---

**`state`** `str`

Playback state.

* `"playing"` -- audio is actively playing
* `"paused"` -- playback has been paused
* `"finished"` -- playback completed successfully
* `"error"` -- an error occurred during playback

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import PlayEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_play(event: PlayEvent):
        print(f"Playback: {event.state}")

    call.on("calling.call.play", handle_play)
    await call.answer()
    await call.play([{"type": "tts", "params": {"text": "Hello!"}}])

client.run()
```

---

### calling.call.record

Emitted when recording state changes on a record operation.

#### RecordEvent

Handlers for this event receive a `RecordEvent` with the following properties:

**`control_id`** `str`

The control ID of the record operation.

---

**`state`** `str`

Recording state.

* `"recording"` -- audio recording is in progress
* `"paused"` -- recording has been paused
* `"finished"` -- recording completed successfully
* `"no_input"` -- recording ended due to no audio input detected

---

**`url`** `str`

URL of the recording file (available when `state` is `"finished"`).

---

**`duration`** `float` — default: 0.0

Recording duration in seconds.

---

**`size`** `int` — default: 0

Recording file size in bytes.

---

**`record`** `dict[str, Any]`

Full record metadata object containing `url`, `duration`, `size`, and other fields.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import RecordEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_record(event: RecordEvent):
        print(f"Recording {event.state}: {event.url}")

    call.on("calling.call.record", handle_record)
    await call.answer()
    await call.record({"direction": "both"})

client.run()
```

---

### calling.call.collect

Emitted when input collection state changes on a
[`collect()`][collect] or
[`play_and_collect()`][playandcollect]
operation.

#### CollectEvent

Handlers for this event receive a `CollectEvent` with the following properties:

**`control_id`** `str`

The control ID of the collect operation.

---

**`state`** `str`

Collection state.

* `"finished"` -- input was collected successfully
* `"error"` -- an error occurred during collection
* `"no_input"` -- no input was detected within the timeout
* `"no_match"` -- collected input did not match any configured patterns

---

**`result`** `dict[str, Any]`

The collected input result. Contains `type` and the collected value.

* `"digit"` -- DTMF digit input was collected
* `"speech"` -- speech input was collected

---

**`final`** `bool | None` — default: None

Whether this is the final result. May be `None` for non-continuous collect operations.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import CollectEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_collect(event: CollectEvent):
        print(f"Collected: {event.result}")

    call.on("calling.call.collect", handle_collect)
    await call.answer()
    await call.play_and_collect(
        media=[{"type": "tts", "params": {"text": "Press 1 or 2."}}],
        collect={"digits": {"max": 1}},
    )

client.run()
```

---

### calling.call.connect

Emitted when a connect operation changes state.

#### ConnectEvent

Handlers for this event receive a `ConnectEvent` with the following properties:

**`connect_state`** `str`

Connection state.

* `"connecting"` -- bridge is being established to the destination
* `"connected"` -- bridge has been successfully established
* `"disconnected"` -- bridge has been disconnected
* `"failed"` -- connection attempt failed

---

**`peer`** `dict[str, Any]`

Information about the connected peer call.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import ConnectEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_connect(event: ConnectEvent):
        print(f"Connection: {event.connect_state}")

    call.on("calling.call.connect", handle_connect)
    await call.answer()
    await call.connect(
        devices=[[{"type": "phone", "params": {"to_number": "+15559876543", "from_number": "+15551234567"}}]]
    )

client.run()
```

---

### calling.call.detect

Emitted when detection results arrive from a detect operation.

#### DetectEvent

Handlers for this event receive a `DetectEvent` with the following properties:

**`control_id`** `str`

The control ID of the detect operation.

---

**`detect`** `dict[str, Any]`

Detection result. Structure depends on the detection type.

* `"machine"` -- voicemail or answering machine detection
* `"fax"` -- fax tone detection
* `"digit"` -- DTMF digit detection

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import DetectEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_detect(event: DetectEvent):
        print(f"Detected: {event.detect}")

    call.on("calling.call.detect", handle_detect)
    await call.answer()
    await call.detect({"type": "machine", "params": {"initial_timeout": 5.0}})

client.run()
```

---

### calling.call.fax

Emitted when a [`send_fax()`][sendfax] or
[`receive_fax()`][receivefax] operation
changes state.

#### FaxEvent

Handlers for this event receive a `FaxEvent` with the following properties:

**`control_id`** `str`

The control ID of the fax operation.

---

**`fax`** `dict[str, Any]`

Fax result data including pages, status, and document URL.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import FaxEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_fax(event: FaxEvent):
        print(f"Fax: {event.fax}")

    call.on("calling.call.fax", handle_fax)
    await call.answer()
    await call.send_fax(document="https://example.com/invoice.pdf")

client.run()
```

---

### calling.call.tap

Emitted when a tap operation changes state.

#### TapEvent

Handlers for this event receive a `TapEvent` with the following properties:

**`control_id`** `str`

The control ID of the tap operation.

---

**`state`** `str`

Tap state.

* `"finished"` -- tap operation completed

---

**`tap`** `dict[str, Any]`

Tap configuration details.

---

**`device`** `dict[str, Any]`

The device receiving the tapped media.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import TapEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_tap(event: TapEvent):
        print(f"Tap {event.state}: {event.tap}")

    call.on("calling.call.tap", handle_tap)
    await call.answer()

client.run()
```

---

### calling.call.stream

Emitted when a stream operation changes state.

#### StreamEvent

Handlers for this event receive a `StreamEvent` with the following properties:

**`control_id`** `str`

The control ID of the stream operation.

---

**`state`** `str`

Stream state.

* `"streaming"` -- stream operation started
* `"finished"` -- stream operation completed

---

**`url`** `str`

The WebSocket URL the stream is connected to.

---

**`name`** `str`

The stream name.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import StreamEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_stream(event: StreamEvent):
        print(f"Stream {event.state}: {event.url}")

    call.on("calling.call.stream", handle_stream)
    await call.answer()

client.run()
```

---

### calling.call.send_digits

Emitted when a send_digits operation changes state.

#### SendDigitsEvent

Handlers for this event receive a `SendDigitsEvent` with the following properties:

**`control_id`** `str`

The control ID of the send_digits operation.

---

**`state`** `str`

Send digits state.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import SendDigitsEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_digits(event: SendDigitsEvent):
        print(f"Send digits: {event.state}")

    call.on("calling.call.send_digits", handle_digits)
    await call.answer()

client.run()
```

---

### calling.call.dial

Emitted during outbound dial state changes. This event is dispatched at the
client level rather than through `call.on()`.

#### DialEvent

Handlers for this event receive a `DialEvent` with the following properties:

**`tag`** `str`

Correlation tag for the dial operation.

---

**`dial_state`** `str`

Dial state.

* `"answered"` -- outbound call was answered
* `"failed"` -- outbound call failed to connect

---

**`call`** `dict[str, Any]`

Call information for the dialed leg.

---

### calling.call.refer

Emitted when a SIP REFER operation changes state.

#### ReferEvent

Handlers for this event receive a `ReferEvent` with the following properties:

**`state`** `str`

Refer state.

---

**`sip_refer_to`** `str`

The SIP URI the call was referred to.

---

**`sip_refer_response_code`** `str`

SIP response code from the REFER request.

---

**`sip_notify_response_code`** `str`

SIP response code from the NOTIFY message.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import ReferEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_refer(event: ReferEvent):
        print(f"Refer to {event.sip_refer_to}: {event.state}")

    call.on("calling.call.refer", handle_refer)
    await call.answer()

client.run()
```

---

### calling.call.hold

Emitted when hold state changes via
[`hold()`][hold] or
[`unhold()`][unhold].

#### HoldEvent

Handlers for this event receive a `HoldEvent` with the following properties:

**`state`** `str`

Hold state.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import HoldEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_hold(event: HoldEvent):
        print(f"Hold: {event.state}")

    call.on("calling.call.hold", handle_hold)
    await call.answer()

client.run()
```

---

### calling.call.denoise

Emitted when noise reduction state changes.

#### DenoiseEvent

Handlers for this event receive a `DenoiseEvent` with the following properties:

**`denoised`** `bool` — default: False

Whether noise reduction is currently active.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import DenoiseEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_denoise(event: DenoiseEvent):
        print(f"Denoise active: {event.denoised}")

    call.on("calling.call.denoise", handle_denoise)
    await call.answer()

client.run()
```

---

### calling.call.pay

Emitted when a pay operation changes state.

#### PayEvent

Handlers for this event receive a `PayEvent` with the following properties:

**`control_id`** `str`

The control ID of the pay operation.

---

**`state`** `str`

Payment state.

* `"finished"` -- payment collection completed
* `"error"` -- payment operation failed

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import PayEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_pay(event: PayEvent):
        print(f"Payment: {event.state}")

    call.on("calling.call.pay", handle_pay)
    await call.answer()

client.run()
```

---

### calling.call.echo

Emitted when an echo operation changes state.

#### EchoEvent

Handlers for this event receive an `EchoEvent` with the following properties:

**`state`** `str`

Echo state.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import EchoEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_echo(event: EchoEvent):
        print(f"Echo: {event.state}")

    call.on("calling.call.echo", handle_echo)
    await call.answer()

client.run()
```

---

### calling.call.queue

Emitted when queue state changes via
[`queue_enter()`][queueenter] or
[`queue_leave()`][queueleave].

#### QueueEvent

Handlers for this event receive a `QueueEvent` with the following properties:

**`control_id`** `str`

The control ID of the queue operation.

---

**`status`** `str`

Queue status.

---

**`queue_id`** `str`

The queue identifier.

---

**`queue_name`** `str`

The queue name.

---

**`position`** `int` — default: 0

Current position in the queue.

---

**`size`** `int` — default: 0

Total number of calls in the queue.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import QueueEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_queue(event: QueueEvent):
        print(f"Queue {event.queue_name}: position {event.position}/{event.size}")

    call.on("calling.call.queue", handle_queue)
    await call.answer()

client.run()
```

---

### calling.conference

Emitted when conference state changes.

#### ConferenceEvent

Handlers for this event receive a `ConferenceEvent` with the following properties:

**`conference_id`** `str`

The conference identifier.

---

**`name`** `str`

The conference name.

---

**`status`** `str`

Conference status.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import ConferenceEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_conference(event: ConferenceEvent):
        print(f"Conference {event.name}: {event.status}")

    call.on("calling.conference", handle_conference)
    await call.answer()

client.run()
```

---

### calling.call.transcribe

Emitted when a transcribe operation changes state.

> **Info**
>
> Background transcription is implemented as a recording with transcription enabled, so this event reports the underlying recording (its `url`, `recording_id`, `duration`, and `size`).

#### TranscribeEvent

Handlers for this event receive a `TranscribeEvent` with the following properties:

**`control_id`** `str`

The control ID of the transcribe operation.

---

**`state`** `str`

Transcription state.

* `"transcribing"` -- transcription has started
* `"finished"` -- transcription completed; `duration`, `size`, `start_time`, and `end_time` are included

---

**`url`** `str`

URL where the recording is hosted. Present once available.

---

**`recording_id`** `str`

ID of the recording.

---

**`duration`** `float` — default: 0.0

Duration of the recording in seconds. Present on the `finished` event.

---

**`size`** `int` — default: 0

Size of the recording in bytes. Present on the `finished` event.

---

> **Info**
>
> The raw event payload (`event.params`) also carries `status_url` (when a status URL
> was set) and, on the `finished` event, `start_time` and `end_time` (Unix epoch seconds).
> These are not yet exposed as typed attributes on `TranscribeEvent`.

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import TranscribeEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_transcribe(event: TranscribeEvent):
        print(f"Transcription {event.state}: {event.url}")

    call.on("calling.call.transcribe", handle_transcribe)
    await call.answer()

client.run()
```

---

### calling.error

Emitted when an error occurs on the call.

#### CallingErrorEvent

Handlers for this event receive a `CallingErrorEvent` with the following properties:

**`code`** `str`

Error code.

---

**`message`** `str`

Human-readable error description.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import CallingErrorEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    def handle_error(event: CallingErrorEvent):
        print(f"Error {event.code}: {event.message}")

    call.on("calling.error", handle_error)
    await call.answer()

client.run()
```

---

## Messaging Events

### messaging.receive

Emitted when an inbound SMS/MMS message is received on a subscribed context.
This event is dispatched at the client level via the `@client.on_message`
decorator.

#### MessageReceiveEvent

Handlers for this event receive a `MessageReceiveEvent` with the following properties:

**`message_id`** `str`

Unique identifier for the message.

---

**`context`** `str`

The messaging context the message was received on.

---

**`direction`** `str`

Always `"inbound"` for receive events.

---

**`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]`

Media URLs for MMS messages.

---

**`segments`** `int` — default: 0

Number of SMS segments.

---

**`message_state`** `str`

State of the message (typically `"received"`).

---

**`tags`** `list[str]`

Tags associated with the message.

---

### messaging.state

Emitted when an outbound message's state changes (e.g., `queued` -> `sent` -> `delivered`).

#### MessageStateEvent

Handlers for this event receive a `MessageStateEvent` with the following properties:

**`message_id`** `str`

Unique identifier for the message.

---

**`context`** `str`

The messaging context.

---

**`direction`** `str`

Always `"outbound"` for state events.

---

**`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]`

Media URLs for MMS messages.

---

**`segments`** `int` — default: 0

Number of SMS segments.

---

**`message_state`** `str`

Current message state.

* `"queued"` -- message accepted by the platform, waiting to be sent
* `"initiated"` -- message sending has been initiated
* `"sent"` -- message has been sent to the carrier
* `"delivered"` -- message has been delivered to the recipient
* `"undelivered"` -- message could not be delivered
* `"failed"` -- message sending failed

---

**`reason`** `str`

Failure reason if the message failed or was undelivered.

---

**`tags`** `list[str]`

Tags associated with the message.

---

#### Example

```python
from signalwire.relay import RelayClient
from signalwire.relay.event import MessageStateEvent

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    contexts=["default"],
)

@client.on_message
async def handle_message(message):
    def handle_state(event: MessageStateEvent):
        print(f"Message {event.message_id}: {event.message_state}")

    message.on(handle_state)

client.run()
```

---

## Event Type Mapping

Reference table mapping `event_type` strings to their typed event classes.

| Event Type                 | Class                 |
| -------------------------- | --------------------- |
| `calling.call.state`       | `CallStateEvent`      |
| `calling.call.receive`     | `CallReceiveEvent`    |
| `calling.call.play`        | `PlayEvent`           |
| `calling.call.record`      | `RecordEvent`         |
| `calling.call.collect`     | `CollectEvent`        |
| `calling.call.connect`     | `ConnectEvent`        |
| `calling.call.detect`      | `DetectEvent`         |
| `calling.call.fax`         | `FaxEvent`            |
| `calling.call.tap`         | `TapEvent`            |
| `calling.call.stream`      | `StreamEvent`         |
| `calling.call.send_digits` | `SendDigitsEvent`     |
| `calling.call.dial`        | `DialEvent`           |
| `calling.call.refer`       | `ReferEvent`          |
| `calling.call.denoise`     | `DenoiseEvent`        |
| `calling.call.pay`         | `PayEvent`            |
| `calling.call.queue`       | `QueueEvent`          |
| `calling.call.echo`        | `EchoEvent`           |
| `calling.call.transcribe`  | `TranscribeEvent`     |
| `calling.call.hold`        | `HoldEvent`           |
| `calling.conference`       | `ConferenceEvent`     |
| `calling.error`            | `CallingErrorEvent`   |
| `messaging.receive`        | `MessageReceiveEvent` |
| `messaging.state`          | `MessageStateEvent`   |