> 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/typescript/relay/call/on [message-on]: /docs/server-sdks/reference/typescript/relay/message [constants]: /docs/server-sdks/reference/typescript/relay/constants [playaction]: /docs/server-sdks/reference/typescript/relay/actions [collect]: /docs/server-sdks/reference/typescript/relay/call/collect [playandcollect]: /docs/server-sdks/reference/typescript/relay/call/play-and-collect [sendfax]: /docs/server-sdks/reference/typescript/relay/call/send-fax [receivefax]: /docs/server-sdks/reference/typescript/relay/call/receive-fax [hold]: /docs/server-sdks/reference/typescript/relay/call/hold [unhold]: /docs/server-sdks/reference/typescript/relay/call/unhold [queueenter]: /docs/server-sdks/reference/typescript/relay/call/queue-enter [queueleave]: /docs/server-sdks/reference/typescript/relay/call/queue-leave Relay events are delivered as typed class 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 [`parseEvent()`](#parseevent) helper to manually parse raw event payloads. ```typescript {14} import { RelayClient, CallStateEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleState = (event: CallStateEvent) => { console.log(`Call state: ${event.callState}`); }; call.on('calling.call.state', handleState); await call.answer(); }); await 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. **`eventType`** `string` The event type string (e.g., `"calling.call.state"`, `"messaging.receive"`). See [`Constants`][constants] for the full list. --- **`params`** `Record` The raw parameters dictionary from the event payload. Contains all event-specific fields, including those not surfaced as typed attributes on subclasses. --- **`callId`** `string` The call identifier associated with this event, if applicable. --- **`timestamp`** `number` — default: 0.0 Server timestamp of the event. --- ## **Methods** ### fromPayload Static method. Parse a raw event object into a `RelayEvent` instance. #### Parameters **`payload`** `Record` — required Raw event payload dictionary. --- **`payload.event_type`** `string` — required The event type string (e.g., `"calling.call.state"`). --- **`payload.params`** `Record` — required Event-specific parameters. --- #### Returns `RelayEvent` -- A new event instance with typed properties. #### Example ```typescript {3} import { RelayEvent } from '@signalwire/sdk'; const event = RelayEvent.fromPayload({ event_type: 'calling.call.state', params: { call_state: 'answered' }, }); console.log(event.eventType); // "calling.call.state" ``` --- ### parseEvent Standalone helper function. Parses a raw event object and returns the appropriate typed event subclass based on the `event_type` field. Falls back to `RelayEvent` for unrecognized event types. #### Parameters **`payload`** `Record` — required Raw event payload dictionary. --- **`payload.event_type`** `string` — required The event type string (e.g., `"calling.call.state"`, `"calling.call.play"`). Determines which typed subclass is returned. --- **`payload.params`** `Record` — 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 ```typescript {3} import { parseEvent, PlayEvent } from '@signalwire/sdk'; const event = parseEvent({ event_type: 'calling.call.play', params: { control_id: 'abc-123', state: 'playing' }, }); // Returns a PlayEvent instance — narrow the type to access subclass properties if (event instanceof PlayEvent) { console.log(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: **`callState`** `string` 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 --- **`endReason`** `string` Reason the call ended. Only present when `callState` 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`** `string` Call direction. * `"inbound"` -- incoming call * `"outbound"` -- outgoing call --- **`device`** `Record` Device information for the call endpoint. --- #### Example ```typescript {14} import { RelayClient, CallStateEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleState = (event: CallStateEvent) => { console.log(`State: ${event.callState}, reason: ${event.endReason}`); }; call.on('calling.call.state', handleState); await call.answer(); }); await 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 `client.onCall()` rather than through `call.on()`. #### CallReceiveEvent Handlers for this event receive a `CallReceiveEvent` with the following properties: **`callState`** `string` Initial call state (typically `"ringing"`). --- **`direction`** `string` Always `"inbound"` for receive events. --- **`device`** `Record` Device information for the caller. --- **`nodeId`** `string` Relay node handling this call. --- **`projectId`** `string` SignalWire project ID. --- **`context`** `string` The context the call was received on. --- **`segmentId`** `string` Call segment identifier. --- **`tag`** `string` 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: **`controlId`** `string` The control ID of the play operation. Matches the `control_id` on the [`PlayAction`][playaction]. --- **`state`** `string` Playback state. * `"playing"` -- audio is actively playing * `"paused"` -- playback has been paused * `"finished"` -- playback completed successfully * `"error"` -- an error occurred during playback --- #### Example ```typescript {14} import { RelayClient, PlayEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handlePlay = (event: PlayEvent) => { console.log(`Playback: ${event.state}`); }; call.on('calling.call.play', handlePlay); await call.answer(); await call.play([{ type: 'tts', text: 'Hello!' }]); }); await 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: **`controlId`** `string` The control ID of the record operation. --- **`state`** `string` 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`** `string` URL of the recording file (available when `state` is `"finished"`). --- **`duration`** `number` — default: 0.0 Recording duration in seconds. --- **`size`** `number` — default: 0 Recording file size in bytes. --- **`record`** `Record` Full record metadata object containing `url`, `duration`, `size`, and other fields. --- #### Example ```typescript {14} import { RelayClient, RecordEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleRecord = (event: RecordEvent) => { console.log(`Recording ${event.state}: ${event.url}`); }; call.on('calling.call.record', handleRecord); await call.answer(); await call.record({ direction: 'both' }); }); await client.run(); ``` --- ### calling.call.collect Emitted when input collection state changes on a [`collect()`][collect] or [`playAndCollect()`][playandcollect] operation. #### CollectEvent Handlers for this event receive a `CollectEvent` with the following properties: **`controlId`** `string` The control ID of the collect operation. --- **`state`** `string` 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`** `Record` The collected input result. Contains `type` and the collected value. * `"digit"` -- DTMF digit input was collected * `"speech"` -- speech input was collected --- **`final`** `boolean | undefined` Whether this is the final result. May be `undefined` for non-continuous collect operations. --- #### Example ```typescript {14} import { RelayClient, CollectEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleCollect = (event: CollectEvent) => { console.log(`Collected: ${JSON.stringify(event.result)}`); }; call.on('calling.call.collect', handleCollect); await call.answer(); await call.playAndCollect( [{ type: 'tts', text: 'Press 1 or 2.' }], { digits: { max: 1 } } ); }); await client.run(); ``` --- ### calling.call.connect Emitted when a connect operation changes state. #### ConnectEvent Handlers for this event receive a `ConnectEvent` with the following properties: **`connectState`** `string` 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`** `Record` Information about the connected peer call. --- #### Example ```typescript {14} import { RelayClient, ConnectEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleConnect = (event: ConnectEvent) => { console.log(`Connection: ${event.connectState}`); }; call.on('calling.call.connect', handleConnect); await call.answer(); await call.connect( [[{ type: 'phone', params: { to_number: '+15559876543', from_number: '+15551234567' } }]] ); }); await 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: **`controlId`** `string` The control ID of the detect operation. --- **`detect`** `Record` Detection result. Structure depends on the detection type. * `"machine"` -- voicemail or answering machine detection * `"fax"` -- fax tone detection * `"digit"` -- DTMF digit detection --- #### Example ```typescript {14} import { RelayClient, DetectEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleDetect = (event: DetectEvent) => { console.log(`Detected: ${JSON.stringify(event.detect)}`); }; call.on('calling.call.detect', handleDetect); await call.answer(); await call.detect({ type: 'machine', params: { initial_timeout: 5.0 } }); }); await client.run(); ``` --- ### calling.call.fax Emitted when a [`sendFax()`][sendfax] or [`receiveFax()`][receivefax] operation changes state. #### FaxEvent Handlers for this event receive a `FaxEvent` with the following properties: **`controlId`** `string` The control ID of the fax operation. --- **`fax`** `Record` Fax result data including pages, status, and document URL. --- #### Example ```typescript {14} import { RelayClient, FaxEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleFax = (event: FaxEvent) => { console.log(`Fax: ${JSON.stringify(event.fax)}`); }; call.on('calling.call.fax', handleFax); await call.answer(); await call.sendFax('https://example.com/invoice.pdf'); }); await client.run(); ``` --- ### calling.call.tap Emitted when a tap operation changes state. #### TapEvent Handlers for this event receive a `TapEvent` with the following properties: **`controlId`** `string` The control ID of the tap operation. --- **`state`** `string` Tap state. * `"finished"` -- tap operation completed --- **`tap`** `Record` Tap configuration details. --- **`device`** `Record` The device receiving the tapped media. --- #### Example ```typescript {14} import { RelayClient, TapEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleTap = (event: TapEvent) => { console.log(`Tap ${event.state}: ${JSON.stringify(event.tap)}`); }; call.on('calling.call.tap', handleTap); await call.answer(); }); await client.run(); ``` --- ### calling.call.stream Emitted when a stream operation changes state. #### StreamEvent Handlers for this event receive a `StreamEvent` with the following properties: **`controlId`** `string` The control ID of the stream operation. --- **`state`** `string` Stream state. * `"streaming"` -- stream operation started * `"finished"` -- stream operation completed --- **`url`** `string` The WebSocket URL the stream is connected to. --- **`name`** `string` The stream name. --- #### Example ```typescript {14} import { RelayClient, StreamEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleStream = (event: StreamEvent) => { console.log(`Stream ${event.state}: ${event.url}`); }; call.on('calling.call.stream', handleStream); await call.answer(); }); await 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: **`controlId`** `string` The control ID of the send\_digits operation. --- **`state`** `string` Send digits state. --- #### Example ```typescript {14} import { RelayClient, SendDigitsEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleDigits = (event: SendDigitsEvent) => { console.log(`Send digits: ${event.state}`); }; call.on('calling.call.send_digits', handleDigits); await call.answer(); }); await 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`** `string` Correlation tag for the dial operation. --- **`dialState`** `string` Dial state. * `"answered"` -- outbound call was answered * `"failed"` -- outbound call failed to connect --- **`call`** `Record` 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`** `string` Refer state. --- **`sipReferTo`** `string` The SIP URI the call was referred to. --- **`sipReferResponseCode`** `string` SIP response code from the REFER request. --- **`sipNotifyResponseCode`** `string` SIP response code from the NOTIFY message. --- #### Example ```typescript {14} import { RelayClient, ReferEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleRefer = (event: ReferEvent) => { console.log(`Refer to ${event.sipReferTo}: ${event.state}`); }; call.on('calling.call.refer', handleRefer); await call.answer(); }); await 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`** `string` Hold state. --- #### Example ```typescript {14} import { RelayClient, HoldEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleHold = (event: HoldEvent) => { console.log(`Hold: ${event.state}`); }; call.on('calling.call.hold', handleHold); await call.answer(); }); await client.run(); ``` --- ### calling.call.denoise Emitted when noise reduction state changes. #### DenoiseEvent Handlers for this event receive a `DenoiseEvent` with the following properties: **`denoised`** `boolean` — default: false Whether noise reduction is currently active. --- #### Example ```typescript {14} import { RelayClient, DenoiseEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleDenoise = (event: DenoiseEvent) => { console.log(`Denoise active: ${event.denoised}`); }; call.on('calling.call.denoise', handleDenoise); await call.answer(); }); await client.run(); ``` --- ### calling.call.pay Emitted when a pay operation changes state. #### PayEvent Handlers for this event receive a `PayEvent` with the following properties: **`controlId`** `string` The control ID of the pay operation. --- **`state`** `string` Payment state. * `"finished"` -- payment collection completed * `"error"` -- payment operation failed --- #### Example ```typescript {14} import { RelayClient, PayEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handlePay = (event: PayEvent) => { console.log(`Payment: ${event.state}`); }; call.on('calling.call.pay', handlePay); await call.answer(); }); await 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`** `string` Echo state. --- #### Example ```typescript {14} import { RelayClient, EchoEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleEcho = (event: EchoEvent) => { console.log(`Echo: ${event.state}`); }; call.on('calling.call.echo', handleEcho); await call.answer(); }); await client.run(); ``` --- ### calling.call.queue Emitted when queue state changes via [`queueEnter()`][queueenter] or [`queueLeave()`][queueleave]. #### QueueEvent Handlers for this event receive a `QueueEvent` with the following properties: **`controlId`** `string` The control ID of the queue operation. --- **`status`** `string` Queue status. --- **`queueId`** `string` The queue identifier. --- **`queueName`** `string` The queue name. --- **`position`** `number` — default: 0 Current position in the queue. --- **`size`** `number` — default: 0 Total number of calls in the queue. --- #### Example ```typescript {14} import { RelayClient, QueueEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleQueue = (event: QueueEvent) => { console.log(`Queue ${event.queueName}: position ${event.position}/${event.size}`); }; call.on('calling.call.queue', handleQueue); await call.answer(); }); await client.run(); ``` --- ### calling.conference Emitted when conference state changes. #### ConferenceEvent Handlers for this event receive a `ConferenceEvent` with the following properties: **`conferenceId`** `string` The conference identifier. --- **`name`** `string` The conference name. --- **`status`** `string` Conference status. --- #### Example ```typescript {14} import { RelayClient, ConferenceEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleConference = (event: ConferenceEvent) => { console.log(`Conference ${event.name}: ${event.status}`); }; call.on('calling.conference', handleConference); await call.answer(); }); await 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`, `recordingId`, `duration`, and `size`). #### TranscribeEvent Handlers for this event receive a `TranscribeEvent` with the following properties: **`controlId`** `string` The control ID of the transcribe operation. --- **`state`** `string` Transcription state. * `"transcribing"` -- transcription has started * `"finished"` -- transcription completed; `duration`, `size`, `start_time`, and `end_time` are included --- **`url`** `string` URL where the recording is hosted. Present once available. --- **`recordingId`** `string` ID of the recording. --- **`duration`** `number` — default: 0.0 Duration of the recording in seconds. Present on the `finished` event. --- **`size`** `number` — 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 properties on `TranscribeEvent`. #### Example ```typescript {14} import { RelayClient, TranscribeEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleTranscribe = (event: TranscribeEvent) => { console.log(`Transcription ${event.state}: ${event.url}`); }; call.on('calling.call.transcribe', handleTranscribe); await call.answer(); }); await 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`** `string` Error code. --- **`message`** `string` Human-readable error description. --- #### Example ```typescript {14} import { RelayClient, CallingErrorEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onCall(async (call) => { const handleError = (event: CallingErrorEvent) => { console.log(`Error ${event.code}: ${event.message}`); }; call.on('calling.error', handleError); await call.answer(); }); await 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 `client.onMessage()`. #### MessageReceiveEvent Handlers for this event receive a `MessageReceiveEvent` with the following properties: **`messageId`** `string` Unique identifier for the message. --- **`context`** `string` The messaging context the message was received on. --- **`direction`** `string` Always `"inbound"` for receive events. --- **`fromNumber`** `string` Sender phone number in E.164 format. --- **`toNumber`** `string` Recipient phone number in E.164 format. --- **`body`** `string` Text content of the message. --- **`media`** `string[]` Media URLs for MMS messages. --- **`segments`** `number` — default: 0 Number of SMS segments. --- **`messageState`** `string` State of the message (typically `"received"`). --- **`tags`** `string[]` 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: **`messageId`** `string` Unique identifier for the message. --- **`context`** `string` The messaging context. --- **`direction`** `string` Always `"outbound"` for state events. --- **`fromNumber`** `string` Sender phone number in E.164 format. --- **`toNumber`** `string` Recipient phone number in E.164 format. --- **`body`** `string` Text content of the message. --- **`media`** `string[]` Media URLs for MMS messages. --- **`segments`** `number` — default: 0 Number of SMS segments. --- **`messageState`** `string` 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`** `string` Failure reason if the message failed or was undelivered. --- **`tags`** `string[]` Tags associated with the message. --- #### Example ```typescript {14} import { RelayClient, MessageStateEvent } from '@signalwire/sdk'; const client = new RelayClient({ project: process.env.SIGNALWIRE_PROJECT_ID!, token: process.env.SIGNALWIRE_API_TOKEN!, contexts: ['default'] }); client.onMessage(async (message) => { const handleState = (event: MessageStateEvent) => { console.log(`Message ${event.messageId}: ${event.messageState}`); }; message.on(handleState); }); await 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` | > Typed event classes for all Relay events.