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

SignalWire space hostname (e.g., your-space.signalwire.com). Set via constructor or SIGNALWIRE_SPACE environment variable. Defaults to relay.signalwire.com.

contexts
list[str]Defaults to []

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

max_active_calls
Optional[int]Defaults 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

1from signalwire.relay import RelayClient
2from signalwire.relay.call import Call
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: Call) -> None:
13 await call.answer()
14 print(f"Received call: {call.call_id}")
15
16client.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

1from signalwire.relay import RelayClient
2from signalwire.relay.message import Message
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: Message) -> None:
13 print(f"Received message: {message.body}")
14
15client.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:

1import asyncio
2from signalwire.relay import RelayClient
3
4async def main():
5 async with RelayClient(
6 project="your-project-id",
7 token="your-api-token",
8 host="your-space.signalwire.com",
9 contexts=["default"],
10 ) as client:
11 call = await client.dial(
12 devices=[[{"type": "phone", "params": {"to_number": "+15559876543", "from_number": "+15551234567"}}]]
13 )
14 # Automatically disconnects on exit
15
16asyncio.run(main())

Example

1from signalwire.relay import RelayClient
2
3client = RelayClient(
4 project="your-project-id",
5 token="your-api-token",
6 host="your-space.signalwire.com",
7 contexts=["default"],
8)
9
10@client.on_call
11async def handle_call(call):
12 await call.answer()
13 action = await call.play([{"type": "tts", "text": "Hello from RELAY!"}])
14 await action.wait()
15 await call.hangup()
16
17@client.on_message
18async def handle_message(message):
19 print(f"SMS from {message.from_number}: {message.body}")
20
21client.run()