Relay

RelayClient

View as MarkdownOpen in Claude

RelayClient manages a persistent WebSocket connection to SignalWire’s Relay service. It handles authentication, automatic reconnection with exponential backoff, inbound event dispatch, outbound dialing, and SMS/MMS messaging. Use it when you need imperative, event-driven control over calls rather than the declarative AI agent approach.

The client supports two authentication modes: project ID + API token, or JWT token. Credentials can be passed directly or read from environment variables.

Properties

project
str

SignalWire project ID. Set via constructor or SIGNALWIRE_PROJECT_ID environment variable.

token
str

API token for authentication. Set via constructor or SIGNALWIRE_API_TOKEN environment variable.

jwt_token
str

JWT token for alternative authentication. Set via constructor or SIGNALWIRE_JWT_TOKEN environment variable. When provided, project and token are not required.

host
strDefaults to relay.signalwire.com

Relay WebSocket endpoint. The default is the endpoint for SignalWire projects; you do not set your space here. The constructor argument wins, then the SIGNALWIRE_SPACE environment variable, then the default. RestClient reads SIGNALWIRE_SPACE as the REST host, so when it is set for REST, pass host="relay.signalwire.com" to keep the default.

contexts
list[str]Defaults to []

List of contexts to subscribe to for inbound call and message events.

max_active_calls
int | NoneDefaults to 1000

Maximum number of concurrent inbound calls the client will track. Calls arriving beyond this limit are dropped with a log warning. Set via constructor or RELAY_MAX_ACTIVE_CALLS environment variable. Constructor-only — not accessible as a public attribute after initialization.

relay_protocol
str

Server-assigned protocol string from the connect response. Read-only. Used internally for session resumption on reconnect.

Decorators

on_call

from signalwire.relay import RelayClient
from signalwire.relay.call import Call
client = RelayClient(
project="your-project-id",
token="your-api-token",
contexts=["default"],
)
@client.on_call
async def handle_call(call: Call) -> None:
await call.answer()
print(f"Received call: {call.call_id}")
client.run()

Register the inbound call handler. The decorated function is called once for each calling.call.receive event on the subscribed contexts. The function receives a Call object with all call properties and control methods. Only one call handler can be active at a time — calling @client.on_call again replaces the previous handler.

on_message

from signalwire.relay import RelayClient
from signalwire.relay.message import Message
client = RelayClient(
project="your-project-id",
token="your-api-token",
contexts=["default"],
)
@client.on_message
async def handle_message(message: Message) -> None:
print(f"Received message: {message.body}")
client.run()

Register the inbound SMS/MMS message handler. The decorated function is called for each messaging.receive event. The function receives a Message object with message properties and state tracking. Only one message handler can be active at a time — calling @client.on_message again replaces the previous handler.

Methods

Async Context Manager

RelayClient supports async with for scoped connections:

import asyncio
from signalwire.relay import RelayClient
async def main():
async with RelayClient(
project="your-project-id",
token="your-api-token",
contexts=["default"],
) as client:
call = await client.dial(
devices=[[{"type": "phone", "params": {"to_number": "+15559876543", "from_number": "+15551234567"}}]]
)
# Automatically disconnects on exit
asyncio.run(main())

Example

from signalwire.relay import RelayClient
client = RelayClient(
project="your-project-id",
token="your-api-token",
contexts=["default"],
)
@client.on_call
async def handle_call(call):
await call.answer()
action = await call.play([{"type": "tts", "params": {"text": "Hello from Relay!"}}])
await action.wait()
await call.hangup()
@client.on_message
async def handle_message(message):
print(f"SMS from {message.from_number}: {message.body}")
client.run()