> For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

# 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<string, unknown>`

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<string, unknown>` — required

Raw event payload dictionary.

---

**`payload.event_type`** `string` — required

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

---

**`payload.params`** `Record<string, unknown>` — 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<string, unknown>` — 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<string, unknown>` — 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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

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<string, unknown>`

Tap configuration details.

---

**`device`** `Record<string, unknown>`

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<string, unknown>`

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.

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.

---

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