RELAY

Events

View as MarkdownOpen in Claude

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

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

1from signalwire.relay import RelayClient
2from signalwire.relay.event import CallStateEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def on_call(call):
13 def handle_state(event: CallStateEvent):
14 print(f"Call state: {event.call_state}")
15
16 call.on("calling.call.state", handle_state)
17 await call.answer()
18
19client.run()

RelayEvent

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

event_type
str

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

params
dict[str, Any]

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

call_id
str

The call identifier associated with this event, if applicable.

timestamp
floatDefaults to 0.0

Server timestamp of the event.

Methods

from_payload

from_payload(payload) -> RelayEvent

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

Parameters

payload
dict[str, Any]Required

Raw event payload dictionary.

payload.event_type
strRequired

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

payload.params
dict[str, Any]Required

Event-specific parameters.

Returns

RelayEvent — A new event instance with typed properties.

Example

1from signalwire.relay.event import RelayEvent
2
3event = RelayEvent.from_payload({
4 "event_type": "calling.call.state",
5 "params": {"call_state": "answered"},
6})
7print(event.event_type) # "calling.call.state"

parse_event

parse_event(payload) -> RelayEvent

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

Parameters

payload
dict[str, Any]Required

Raw event payload dictionary.

payload.event_type
strRequired

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

payload.params
dict[str, Any]Required

Event-specific parameters. Contents vary by event type.

Returns

RelayEvent — The appropriate typed subclass based on the event_type field, or a base RelayEvent for unrecognized types.

Example

1from signalwire.relay.event import parse_event
2
3event = parse_event({
4 "event_type": "calling.call.play",
5 "params": {"control_id": "abc-123", "state": "playing"},
6})
7# Returns a PlayEvent instance
8print(event.state) # "playing"

Calling Events

calling.call.state

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

CallStateEvent

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

call_state
str

The new call state.

  • "created" — call object has been initialized
  • "ringing" — call is ringing at the destination
  • "answered" — call has been answered and is active
  • "ending" — hangup is in progress
  • "ended" — call has been fully terminated
end_reason
str

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

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

Call direction.

  • "inbound" — incoming call
  • "outbound" — outgoing call
device
dict[str, Any]

Device information for the call endpoint.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import CallStateEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_state(event: CallStateEvent):
14 print(f"State: {event.call_state}, reason: {event.end_reason}")
15
16 call.on("calling.call.state", handle_state)
17 await call.answer()
18
19client.run()

calling.call.receive

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

CallReceiveEvent

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

call_state
str

Initial call state (typically "ringing").

direction
str

Always "inbound" for receive events.

device
dict[str, Any]

Device information for the caller.

node_id
str

RELAY node handling this call.

project_id
str

SignalWire project ID.

context
str

The context the call was received on.

segment_id
str

Call segment identifier.

tag
str

Correlation tag for the call.

calling.call.play

Emitted when playback state changes on a play operation.

PlayEvent

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

control_id
str

The control ID of the play operation. Matches the control_id on the PlayAction.

state
str

Playback state.

  • "playing" — audio is actively playing
  • "paused" — playback has been paused
  • "finished" — playback completed successfully
  • "error" — an error occurred during playback

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import PlayEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_play(event: PlayEvent):
14 print(f"Playback: {event.state}")
15
16 call.on("calling.call.play", handle_play)
17 await call.answer()
18 await call.play([{"type": "tts", "text": "Hello!"}])
19
20client.run()

calling.call.record

Emitted when recording state changes on a record operation.

RecordEvent

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

control_id
str

The control ID of the record operation.

state
str

Recording state.

  • "recording" — audio recording is in progress
  • "paused" — recording has been paused
  • "finished" — recording completed successfully
  • "no_input" — recording ended due to no audio input detected
url
str

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

duration
floatDefaults to 0.0

Recording duration in seconds.

size
intDefaults to 0

Recording file size in bytes.

record
dict[str, Any]

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

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import RecordEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_record(event: RecordEvent):
14 print(f"Recording {event.state}: {event.url}")
15
16 call.on("calling.call.record", handle_record)
17 await call.answer()
18 await call.record({"direction": "both"})
19
20client.run()

calling.call.collect

Emitted when input collection state changes on a collect() or play_and_collect() operation.

CollectEvent

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

control_id
str

The control ID of the collect operation.

state
str

Collection state.

  • "finished" — input was collected successfully
  • "error" — an error occurred during collection
  • "no_input" — no input was detected within the timeout
  • "no_match" — collected input did not match any configured patterns
result
dict[str, Any]

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

  • "digit" — DTMF digit input was collected
  • "speech" — speech input was collected
final
Optional[bool]

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

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import CollectEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_collect(event: CollectEvent):
14 print(f"Collected: {event.result}")
15
16 call.on("calling.call.collect", handle_collect)
17 await call.answer()
18 await call.play_and_collect(
19 media=[{"type": "tts", "text": "Press 1 or 2."}],
20 collect={"digits": {"max": 1}},
21 )
22
23client.run()

calling.call.connect

Emitted when a connect operation changes state.

ConnectEvent

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

connect_state
str

Connection state.

  • "connecting" — bridge is being established to the destination
  • "connected" — bridge has been successfully established
  • "disconnected" — bridge has been disconnected
  • "failed" — connection attempt failed
peer
dict[str, Any]

Information about the connected peer call.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import ConnectEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_connect(event: ConnectEvent):
14 print(f"Connection: {event.connect_state}")
15
16 call.on("calling.call.connect", handle_connect)
17 await call.answer()
18 await call.connect(
19 devices=[[{"type": "phone", "params": {"to_number": "+15559876543", "from_number": "+15551234567"}}]]
20 )
21
22client.run()

calling.call.detect

Emitted when detection results arrive from a detect operation.

DetectEvent

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

control_id
str

The control ID of the detect operation.

detect
dict[str, Any]

Detection result. Structure depends on the detection type.

  • "machine" — voicemail or answering machine detection
  • "fax" — fax tone detection
  • "digit" — DTMF digit detection

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import DetectEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_detect(event: DetectEvent):
14 print(f"Detected: {event.detect}")
15
16 call.on("calling.call.detect", handle_detect)
17 await call.answer()
18 await call.detect({"type": "machine", "params": {"initial_timeout": 5.0}})
19
20client.run()

calling.call.fax

Emitted when a send_fax() or receive_fax() operation changes state.

FaxEvent

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

control_id
str

The control ID of the fax operation.

fax
dict[str, Any]

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

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import FaxEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_fax(event: FaxEvent):
14 print(f"Fax: {event.fax}")
15
16 call.on("calling.call.fax", handle_fax)
17 await call.answer()
18 await call.send_fax(document="https://example.com/invoice.pdf")
19
20client.run()

calling.call.tap

Emitted when a tap operation changes state.

TapEvent

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

control_id
str

The control ID of the tap operation.

state
str

Tap state.

  • "finished" — tap operation completed
tap
dict[str, Any]

Tap configuration details.

device
dict[str, Any]

The device receiving the tapped media.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import TapEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_tap(event: TapEvent):
14 print(f"Tap {event.state}: {event.tap}")
15
16 call.on("calling.call.tap", handle_tap)
17 await call.answer()
18
19client.run()

calling.call.stream

Emitted when a stream operation changes state.

StreamEvent

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

control_id
str

The control ID of the stream operation.

state
str

Stream state.

  • "finished" — stream operation completed
url
str

The WebSocket URL the stream is connected to.

name
str

The stream name.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import StreamEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_stream(event: StreamEvent):
14 print(f"Stream {event.state}: {event.url}")
15
16 call.on("calling.call.stream", handle_stream)
17 await call.answer()
18
19client.run()

calling.call.send_digits

Emitted when a send_digits operation changes state.

SendDigitsEvent

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

control_id
str

The control ID of the send_digits operation.

state
str

Send digits state.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import SendDigitsEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_digits(event: SendDigitsEvent):
14 print(f"Send digits: {event.state}")
15
16 call.on("calling.call.send_digits", handle_digits)
17 await call.answer()
18
19client.run()

calling.call.dial

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

DialEvent

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

tag
str

Correlation tag for the dial operation.

dial_state
str

Dial state.

  • "answered" — outbound call was answered
  • "failed" — outbound call failed to connect
call
dict[str, Any]

Call information for the dialed leg.

calling.call.refer

Emitted when a SIP REFER operation changes state.

ReferEvent

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

state
str

Refer state.

sip_refer_to
str

The SIP URI the call was referred to.

sip_refer_response_code
str

SIP response code from the REFER request.

sip_notify_response_code
str

SIP response code from the NOTIFY message.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import ReferEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_refer(event: ReferEvent):
14 print(f"Refer to {event.sip_refer_to}: {event.state}")
15
16 call.on("calling.call.refer", handle_refer)
17 await call.answer()
18
19client.run()

calling.call.hold

Emitted when hold state changes via hold() or unhold().

HoldEvent

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

state
str

Hold state.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import HoldEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_hold(event: HoldEvent):
14 print(f"Hold: {event.state}")
15
16 call.on("calling.call.hold", handle_hold)
17 await call.answer()
18
19client.run()

calling.call.denoise

Emitted when noise reduction state changes.

DenoiseEvent

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

denoised
boolDefaults to False

Whether noise reduction is currently active.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import DenoiseEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_denoise(event: DenoiseEvent):
14 print(f"Denoise active: {event.denoised}")
15
16 call.on("calling.call.denoise", handle_denoise)
17 await call.answer()
18
19client.run()

calling.call.pay

Emitted when a pay operation changes state.

PayEvent

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

control_id
str

The control ID of the pay operation.

state
str

Payment state.

  • "finished" — payment collection completed
  • "error" — payment operation failed

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import PayEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_pay(event: PayEvent):
14 print(f"Payment: {event.state}")
15
16 call.on("calling.call.pay", handle_pay)
17 await call.answer()
18
19client.run()

calling.call.echo

Emitted when an echo operation changes state.

EchoEvent

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

state
str

Echo state.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import EchoEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_echo(event: EchoEvent):
14 print(f"Echo: {event.state}")
15
16 call.on("calling.call.echo", handle_echo)
17 await call.answer()
18
19client.run()

calling.call.queue

Emitted when queue state changes via queue_enter() or queue_leave().

QueueEvent

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

control_id
str

The control ID of the queue operation.

status
str

Queue status.

queue_id
str

The queue identifier.

queue_name
str

The queue name.

position
intDefaults to 0

Current position in the queue.

size
intDefaults to 0

Total number of calls in the queue.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import QueueEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_queue(event: QueueEvent):
14 print(f"Queue {event.queue_name}: position {event.position}/{event.size}")
15
16 call.on("calling.call.queue", handle_queue)
17 await call.answer()
18
19client.run()

calling.conference

Emitted when conference state changes.

ConferenceEvent

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

conference_id
str

The conference identifier.

name
str

The conference name.

status
str

Conference status.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import ConferenceEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_conference(event: ConferenceEvent):
14 print(f"Conference {event.name}: {event.status}")
15
16 call.on("calling.conference", handle_conference)
17 await call.answer()
18
19client.run()

calling.call.transcribe

Emitted when a transcribe operation changes state.

TranscribeEvent

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

control_id
str

The control ID of the transcribe operation.

state
str

Transcription state.

  • "finished" — transcription completed successfully
url
str

URL of the transcription result.

recording_id
str

Associated recording ID.

duration
floatDefaults to 0.0

Duration of the transcribed audio in seconds.

size
intDefaults to 0

Size of the transcription data in bytes.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import TranscribeEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_transcribe(event: TranscribeEvent):
14 print(f"Transcription {event.state}: {event.url}")
15
16 call.on("calling.call.transcribe", handle_transcribe)
17 await call.answer()
18
19client.run()

calling.error

Emitted when an error occurs on the call.

CallingErrorEvent

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

code
str

Error code.

message
str

Human-readable error description.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import CallingErrorEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_call
12async def handle_call(call):
13 def handle_error(event: CallingErrorEvent):
14 print(f"Error {event.code}: {event.message}")
15
16 call.on("calling.error", handle_error)
17 await call.answer()
18
19client.run()

Messaging Events

messaging.receive

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

MessageReceiveEvent

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

message_id
str

Unique identifier for the message.

context
str

The messaging context the message was received on.

direction
str

Always "inbound" for receive events.

from_number
str

Sender phone number in E.164 format.

to_number
str

Recipient phone number in E.164 format.

body
str

Text content of the message.

media
list[str]

Media URLs for MMS messages.

segments
intDefaults to 0

Number of SMS segments.

message_state
str

State of the message (typically "received").

tags
list[str]

Tags associated with the message.

messaging.state

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

MessageStateEvent

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

message_id
str

Unique identifier for the message.

context
str

The messaging context.

direction
str

Always "outbound" for state events.

from_number
str

Sender phone number in E.164 format.

to_number
str

Recipient phone number in E.164 format.

body
str

Text content of the message.

media
list[str]

Media URLs for MMS messages.

segments
intDefaults to 0

Number of SMS segments.

message_state
str

Current message state.

  • "queued" — message accepted by the platform, waiting to be sent
  • "initiated" — message sending has been initiated
  • "sent" — message has been sent to the carrier
  • "delivered" — message has been delivered to the recipient
  • "undelivered" — message could not be delivered
  • "failed" — message sending failed
reason
str

Failure reason if the message failed or was undelivered.

tags
list[str]

Tags associated with the message.

Example

1from signalwire.relay import RelayClient
2from signalwire.relay.event import MessageStateEvent
3
4client = RelayClient(
5 project="your-project-id",
6 token="your-api-token",
7 host="your-space.signalwire.com",
8 contexts=["default"],
9)
10
11@client.on_message
12async def handle_message(message):
13 def handle_state(event: MessageStateEvent):
14 print(f"Message {event.message_id}: {event.message_state}")
15
16 message.on(handle_state)
17
18client.run()

Event Type Mapping

Reference table mapping event_type strings to their typed event classes.

Event TypeClass
calling.call.stateCallStateEvent
calling.call.receiveCallReceiveEvent
calling.call.playPlayEvent
calling.call.recordRecordEvent
calling.call.collectCollectEvent
calling.call.connectConnectEvent
calling.call.detectDetectEvent
calling.call.faxFaxEvent
calling.call.tapTapEvent
calling.call.streamStreamEvent
calling.call.send_digitsSendDigitsEvent
calling.call.dialDialEvent
calling.call.referReferEvent
calling.call.denoiseDenoiseEvent
calling.call.payPayEvent
calling.call.queueQueueEvent
calling.call.echoEchoEvent
calling.call.transcribeTranscribeEvent
calling.call.holdHoldEvent
calling.conferenceConferenceEvent
calling.errorCallingErrorEvent
messaging.receiveMessageReceiveEvent
messaging.stateMessageStateEvent