RelayClient Reference

Constructor

RelayClient(
    project: str = None,          # SIGNALWIRE_PROJECT_ID
    token: str = None,            # SIGNALWIRE_API_TOKEN
    jwt_token: str = None,        # SIGNALWIRE_JWT_TOKEN
    host: str = None,             # SIGNALWIRE_SPACE (default: relay.signalwire.com)
    contexts: list[str] = None,   # Topics to subscribe to
    max_active_calls: int = None, # RELAY_MAX_ACTIVE_CALLS (default: 1000)
)

Authentication requires either project + token (legacy) or jwt_token (faster, no server roundtrip). All parameters fall back to their corresponding environment variables.

Methods

run()

Blocking entry point. Connects, authenticates, and runs the event loop with auto-reconnect until interrupted.

client.run()

connect() / disconnect()

Manual lifecycle control for use in async code.

await client.connect()
# ... use client ...
await client.disconnect()

Also supports async context manager:

async with RelayClient(contexts=["default"]) as client:
    ...

on_call(handler)

Decorator to register the inbound call handler. The handler receives a Call object.

@client.on_call
async def handle(call):
    await call.answer()

dial(devices, *, tag=None, max_duration=None, dial_timeout=None) -> Call

Place an outbound call. Returns a Call once the remote party answers.

call = await client.dial([
    [{"type": "phone", "params": {"to_number": "+15551234567", "from_number": "+15559876543"}}]
])

on_message(handler)

Decorator to register the inbound message handler. The handler receives a Message object.

@client.on_message
async def handle(message):
    print(f"SMS from {message.from_number}: {message.body}")

send_message(*, to_number, from_number, body=None, media=None, ...) -> Message

Send an outbound SMS/MMS. Returns a Message that tracks delivery state.

message = await client.send_message(
    to_number="+15552222222",
    from_number="+15551111111",
    body="Hello!",
)
event = await message.wait()  # block until delivered/failed

See Messaging for full details.

execute(method, params) -> dict

Send a raw JSON-RPC request. Used internally by Call methods, but available for custom commands.

receive(contexts) / unreceive(contexts)

Dynamically subscribe to or unsubscribe from contexts after connecting.

await client.receive(["new-context"])
await client.unreceive(["old-context"])

Properties

| Property | Type | Description | |----------|------|-------------| | relay_protocol | str | Server-assigned protocol string from connect response | | project | str | Project ID | | host | str | Relay host | | contexts | list[str] | Initial contexts |

Connection Behavior

Concurrency

Each inbound call handler runs as an independent asyncio.Task, so multiple calls are handled concurrently. The max_active_calls parameter (default: 1000) caps concurrent calls to prevent unbounded memory growth.

For multiple WebSocket connections in one process, set RELAY_MAX_CONNECTIONS (default: 1).

Error Handling

from signalwire_agents.relay import RelayError

try:
    await call.play([...])
except RelayError as e:
    print(f"Error {e.code}: {e.message}")

RelayError is raised when the server returns a non-2xx response code. Errors 404 and 410 (call gone) are silently swallowed by Call methods since the call no longer exists.