***

title: Events
slug: /reference/typescript/relay/events
description: Typed event classes for all RELAY events.
max-toc-depth: 3
---------------------

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

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

<ParamField path="eventType" type="string" toc={true}>
  The event type string (e.g., `"calling.call.state"`, `"messaging.receive"`).
  See [`Constants`][constants] for the full list.
</ParamField>

<ParamField path="params" type={"Record<string, unknown>"} toc={true}>
  The raw parameters dictionary from the event payload. Contains all event-specific
  fields, including those not surfaced as typed attributes on subclasses.
</ParamField>

<ParamField path="callId" type="string" default="" toc={true}>
  The call identifier associated with this event, if applicable.
</ParamField>

<ParamField path="timestamp" type="number" default="0.0" toc={true}>
  Server timestamp of the event.
</ParamField>

## **Methods**

### fromPayload

Static method. Parse a raw event object into a `RelayEvent` instance.

#### Parameters

<ParamField path="payload" type={"Record<string, unknown>"} required={true} toc={true}>
  Raw event payload dictionary.
</ParamField>

<Indent>
  <ParamField path="payload.event_type" type="string" required={true} toc={true}>
    The event type string (e.g., `"calling.call.state"`).
  </ParamField>

  <ParamField path="payload.params" type={"Record<string, unknown>"} required={true} toc={true}>
    Event-specific parameters.
  </ParamField>
</Indent>

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

<ParamField path="payload" type={"Record<string, unknown>"} required={true} toc={true}>
  Raw event payload dictionary.
</ParamField>

<Indent>
  <ParamField path="payload.event_type" type="string" required={true} toc={true}>
    The event type string (e.g., `"calling.call.state"`, `"calling.call.play"`).
    Determines which typed subclass is returned.
  </ParamField>

  <ParamField path="payload.params" type={"Record<string, unknown>"} required={true} toc={true}>
    Event-specific parameters. Contents vary by event type.
  </ParamField>
</Indent>

#### 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:

<ParamField path="callState" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="endReason" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="direction" type="string" default="" toc={true}>
  Call direction.

  * `"inbound"` -- incoming call
  * `"outbound"` -- outgoing call
</ParamField>

<ParamField path="device" type={"Record<string, unknown>"} toc={true}>
  Device information for the call endpoint.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="callState" type="string" default="" toc={true}>
  Initial call state (typically `"ringing"`).
</ParamField>

<ParamField path="direction" type="string" default="" toc={true}>
  Always `"inbound"` for receive events.
</ParamField>

<ParamField path="device" type={"Record<string, unknown>"} toc={true}>
  Device information for the caller.
</ParamField>

<ParamField path="nodeId" type="string" default="" toc={true}>
  RELAY node handling this call.
</ParamField>

<ParamField path="projectId" type="string" default="" toc={true}>
  SignalWire project ID.
</ParamField>

<ParamField path="context" type="string" default="" toc={true}>
  The context the call was received on.
</ParamField>

<ParamField path="segmentId" type="string" default="" toc={true}>
  Call segment identifier.
</ParamField>

<ParamField path="tag" type="string" default="" toc={true}>
  Correlation tag for the call.
</ParamField>

### calling.call.play

Emitted when playback state changes on a play operation.

#### PlayEvent

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

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the play operation. Matches the `control_id` on the
  [`PlayAction`][playaction].
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Playback state.

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

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the record operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="url" type="string" default="" toc={true}>
  URL of the recording file (available when `state` is `"finished"`).
</ParamField>

<ParamField path="duration" type="number" default="0.0" toc={true}>
  Recording duration in seconds.
</ParamField>

<ParamField path="size" type="number" default="0" toc={true}>
  Recording file size in bytes.
</ParamField>

<ParamField path="record" type={"Record<string, unknown>"} toc={true}>
  Full record metadata object containing `url`, `duration`, `size`, and other fields.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the collect operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="result" type={"Record<string, unknown>"} toc={true}>
  The collected input result. Contains `type` and the collected value.

  * `"digit"` -- DTMF digit input was collected
  * `"speech"` -- speech input was collected
</ParamField>

<ParamField path="final" type="boolean | undefined" toc={true}>
  Whether this is the final result. May be `undefined` for non-continuous collect operations.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="connectState" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="peer" type={"Record<string, unknown>"} toc={true}>
  Information about the connected peer call.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the detect operation.
</ParamField>

<ParamField path="detect" type={"Record<string, unknown>"} toc={true}>
  Detection result. Structure depends on the detection type.

  * `"machine"` -- voicemail or answering machine detection
  * `"fax"` -- fax tone detection
  * `"digit"` -- DTMF digit detection
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the fax operation.
</ParamField>

<ParamField path="fax" type={"Record<string, unknown>"} toc={true}>
  Fax result data including pages, status, and document URL.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the tap operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Tap state.

  * `"finished"` -- tap operation completed
</ParamField>

<ParamField path="tap" type={"Record<string, unknown>"} toc={true}>
  Tap configuration details.
</ParamField>

<ParamField path="device" type={"Record<string, unknown>"} toc={true}>
  The device receiving the tapped media.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the stream operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Stream state.

  * `"finished"` -- stream operation completed
</ParamField>

<ParamField path="url" type="string" default="" toc={true}>
  The WebSocket URL the stream is connected to.
</ParamField>

<ParamField path="name" type="string" default="" toc={true}>
  The stream name.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the send\_digits operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Send digits state.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="tag" type="string" default="" toc={true}>
  Correlation tag for the dial operation.
</ParamField>

<ParamField path="dialState" type="string" default="" toc={true}>
  Dial state.

  * `"answered"` -- outbound call was answered
  * `"failed"` -- outbound call failed to connect
</ParamField>

<ParamField path="call" type={"Record<string, unknown>"} toc={true}>
  Call information for the dialed leg.
</ParamField>

### calling.call.refer

Emitted when a SIP REFER operation changes state.

#### ReferEvent

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

<ParamField path="state" type="string" default="" toc={true}>
  Refer state.
</ParamField>

<ParamField path="sipReferTo" type="string" default="" toc={true}>
  The SIP URI the call was referred to.
</ParamField>

<ParamField path="sipReferResponseCode" type="string" default="" toc={true}>
  SIP response code from the REFER request.
</ParamField>

<ParamField path="sipNotifyResponseCode" type="string" default="" toc={true}>
  SIP response code from the NOTIFY message.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="state" type="string" default="" toc={true}>
  Hold state.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="denoised" type="boolean" default="false" toc={true}>
  Whether noise reduction is currently active.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the pay operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Payment state.

  * `"finished"` -- payment collection completed
  * `"error"` -- payment operation failed
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="state" type="string" default="" toc={true}>
  Echo state.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the queue operation.
</ParamField>

<ParamField path="status" type="string" default="" toc={true}>
  Queue status.
</ParamField>

<ParamField path="queueId" type="string" default="" toc={true}>
  The queue identifier.
</ParamField>

<ParamField path="queueName" type="string" default="" toc={true}>
  The queue name.
</ParamField>

<ParamField path="position" type="number" default="0" toc={true}>
  Current position in the queue.
</ParamField>

<ParamField path="size" type="number" default="0" toc={true}>
  Total number of calls in the queue.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="conferenceId" type="string" default="" toc={true}>
  The conference identifier.
</ParamField>

<ParamField path="name" type="string" default="" toc={true}>
  The conference name.
</ParamField>

<ParamField path="status" type="string" default="" toc={true}>
  Conference status.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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.

#### TranscribeEvent

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

<ParamField path="controlId" type="string" default="" toc={true}>
  The control ID of the transcribe operation.
</ParamField>

<ParamField path="state" type="string" default="" toc={true}>
  Transcription state.

  * `"finished"` -- transcription completed successfully
</ParamField>

<ParamField path="url" type="string" default="" toc={true}>
  URL of the transcription result.
</ParamField>

<ParamField path="recordingId" type="string" default="" toc={true}>
  Associated recording ID.
</ParamField>

<ParamField path="duration" type="number" default="0.0" toc={true}>
  Duration of the transcribed audio in seconds.
</ParamField>

<ParamField path="size" type="number" default="0" toc={true}>
  Size of the transcription data in bytes.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="code" type="string" default="" toc={true}>
  Error code.
</ParamField>

<ParamField path="message" type="string" default="" toc={true}>
  Human-readable error description.
</ParamField>

#### Example

```typescript {14}
import { RelayClient } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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:

<ParamField path="messageId" type="string" default="" toc={true}>
  Unique identifier for the message.
</ParamField>

<ParamField path="context" type="string" default="" toc={true}>
  The messaging context the message was received on.
</ParamField>

<ParamField path="direction" type="string" default="" toc={true}>
  Always `"inbound"` for receive events.
</ParamField>

<ParamField path="fromNumber" type="string" default="" toc={true}>
  Sender phone number in E.164 format.
</ParamField>

<ParamField path="toNumber" type="string" default="" toc={true}>
  Recipient phone number in E.164 format.
</ParamField>

<ParamField path="body" type="string" default="" toc={true}>
  Text content of the message.
</ParamField>

<ParamField path="media" type="string[]" toc={true}>
  Media URLs for MMS messages.
</ParamField>

<ParamField path="segments" type="number" default="0" toc={true}>
  Number of SMS segments.
</ParamField>

<ParamField path="messageState" type="string" default="" toc={true}>
  State of the message (typically `"received"`).
</ParamField>

<ParamField path="tags" type="string[]" toc={true}>
  Tags associated with the message.
</ParamField>

### 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:

<ParamField path="messageId" type="string" default="" toc={true}>
  Unique identifier for the message.
</ParamField>

<ParamField path="context" type="string" default="" toc={true}>
  The messaging context.
</ParamField>

<ParamField path="direction" type="string" default="" toc={true}>
  Always `"outbound"` for state events.
</ParamField>

<ParamField path="fromNumber" type="string" default="" toc={true}>
  Sender phone number in E.164 format.
</ParamField>

<ParamField path="toNumber" type="string" default="" toc={true}>
  Recipient phone number in E.164 format.
</ParamField>

<ParamField path="body" type="string" default="" toc={true}>
  Text content of the message.
</ParamField>

<ParamField path="media" type="string[]" toc={true}>
  Media URLs for MMS messages.
</ParamField>

<ParamField path="segments" type="number" default="0" toc={true}>
  Number of SMS segments.
</ParamField>

<ParamField path="messageState" type="string" default="" toc={true}>
  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
</ParamField>

<ParamField path="reason" type="string" default="" toc={true}>
  Failure reason if the message failed or was undelivered.
</ParamField>

<ParamField path="tags" type="string[]" toc={true}>
  Tags associated with the message.
</ParamField>

#### Example

```typescript {14}
import { RelayClient, MessageStateEvent } from '@signalwire/sdk';

const client = new RelayClient({
  project: process.env.SIGNALWIRE_PROJECT_ID!,
  token: process.env.SIGNALWIRE_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`   |