SignalWire SDK for Ruby
Build AI voice agents, control live calls over WebSocket, and manage every SignalWire resource over REST -- all from one gem.
What's in this SDK
| Capability | What it does | Quick link |
|---|---|---|
| AI Agents | Build voice agents that handle calls autonomously -- the platform runs the AI pipeline, your code defines the persona, tools, and call flow | Agent Guide |
| RELAY Client | Control live calls and SMS/MMS in real time over WebSocket -- answer, play, record, collect DTMF, conference, transfer, and more | RELAY docs |
| REST Client | Manage SignalWire resources over HTTP -- phone numbers, SIP endpoints, Fabric AI agents, video rooms, messaging, and the full set of API namespaces | REST docs |
gem install signalwire-sdkPublished as
signalwire-sdkon RubyGems. The require path is stillsignalwire, sorequire 'signalwire'works unchanged.
AI Agents
Each agent is a self-contained microservice that generates SWML (SignalWire Markup Language) and handles SWAIG (SignalWire AI Gateway) tool calls. The SignalWire platform runs the entire AI pipeline (STT, LLM, TTS) -- your agent just defines the behavior.
require 'signalwire'
AGENT = SignalWire::AgentBase.new(name: 'my-agent', route: '/')
AGENT.add_language('English', 'en-US', 'elevenlabs.rachel')
AGENT.prompt_add_section('Role', 'You are a helpful assistant.')
AGENT.define_tool(
name: 'get_time',
description: 'Get the current time',
parameters: {}
) do |_args, _raw_data|
SignalWire::Swaig::FunctionResult.new("The time is #{Time.now.strftime('%H:%M:%S')}")
end
AGENT.run if __FILE__ == $PROGRAM_NAMEExposing the agent as the AGENT constant lets swaig-test discover it, and
guarding AGENT.run keeps loading the file for a test from starting a server.
Test locally without running a server:
swaig-test quickstart_agent.rb --simulate-serverless lambda --list-tools
swaig-test quickstart_agent.rb --simulate-serverless lambda --dump-swml
swaig-test quickstart_agent.rb --simulate-serverless lambda --exec get_time--exec NAME runs a tool. Pass each argument as its own --param KEY=VALUE
(values are parsed as JSON — numbers, true/false, and null are typed;
anything else is a string). For a tool get_weather(location):
swaig-test my_agent.rb --simulate-serverless lambda \
--exec get_weather --param location="San Francisco" --param units=metricAgent Features
-
Prompt Object Model (POM) -- structured prompt composition via
prompt_add_section -
SWAIG tools -- define functions with
define_tooland a block that the AI calls mid-conversation, with native access to the call's media stack -
Skills system -- add capabilities with one-liners:
agent.add_skill('datetime') - Contexts and steps -- structured multi-step workflows with navigation control
- DataMap tools -- tools that execute on SignalWire's servers, calling REST APIs without your own webhook
- Dynamic configuration -- per-request agent customization for multi-tenant deployments
- Call flow control -- pre-answer, post-answer, and post-AI verb insertion
- Prefab agents -- ready-to-use archetypes (InfoGatherer, Survey, FAQ, Receptionist, Concierge)
-
Multi-agent hosting -- serve multiple agents on a single server with
AgentServer -
Document search -- vector/keyword search over a remote index via the
native_vector_searchskill (remote HTTP mode; the Ruby port does not ship the offline/embedded backend) - SIP routing -- route SIP calls to agents based on usernames
- Session state -- persistent conversation state with global data and post-prompt summaries
- Security -- auto-generated basic auth, function-specific HMAC tokens, SSL support
- Rack compatible -- run standalone or mount in Rails, Sinatra, or any Rack app
- Serverless -- auto-detects Lambda, CGI, Google Cloud Functions, Azure Functions
Agent Examples
The examples/ directory contains 56 working examples:
| Example | What it demonstrates |
|---|---|
| simple_agent.rb | POM prompts, SWAIG tools, hints, language config, LLM tuning |
| contexts_demo.rb | Multi-step workflow with context switching and step navigation |
| datamap_demo.rb | Server-side API tools without webhooks |
| skills_demo.rb | Loading built-in skills (datetime, math, joke) |
| call_flow_and_actions_demo.rb | Call flow verbs, debug events, FunctionResult actions |
| session_and_state_demo.rb | Global data, post-prompt analysis, on_summary callback |
| multi_agent_server.rb | Multiple agents on one server with AgentServer |
| lambda_agent.rb | Serverless deployment with exportable Rack app |
| comprehensive_dynamic_agent.rb | Per-request dynamic configuration, multi-tenant routing |
See examples/README.md for the full list organized by category.
RELAY Client
Real-time call control and messaging over WebSocket. The RELAY client connects to SignalWire via the Blade protocol and gives you threaded, imperative control over live phone calls and SMS/MMS.
require 'signalwire'
require 'signalwire/relay/client'
client = SignalWire::Relay::Client.new(
project: 'your-project-id',
token: 'your-api-token',
space: 'example.signalwire.com',
contexts: ['default']
)
client.on_call do |call|
call.answer
action = call.play([{ 'type' => 'tts', 'params' => { 'text' => 'Welcome!' } }])
action.wait
call.hangup
end
client.run- 57+ calling methods (play, record, collect, detect, tap, stream, AI, conferencing, and more)
- SMS/MMS messaging with delivery tracking
- Action objects with
wait,stop,pause,resume - Thread-safe with auto-reconnect and exponential backoff
See the RELAY documentation for the full guide, API reference, and examples.
REST Client
Synchronous REST client for managing SignalWire resources and controlling calls over HTTP. No WebSocket required.
require 'signalwire'
require 'signalwire/rest/rest_client'
client = SignalWire::REST::RestClient.new(
project: 'your-project-id',
token: 'your-api-token',
host: 'example.signalwire.com'
)
client.fabric.ai_agents.create(name: 'Support Bot', prompt: { 'text' => 'You are helpful.' })
client.calling.play(call_id, play: [{ 'type' => 'tts', 'params' => { 'text' => 'Hello!' } }])
client.phone_numbers.search(areacode: '512')
client.datasphere.documents.search(query_string: 'billing policy')- Full namespaced API surface: Fabric (13 resource types), Calling (37 commands), Video, Datasphere, Phone Numbers, SIP, Queues, Recordings, and more
- Hash returns -- raw JSON, no wrapper objects to learn
- Single
RestClientwith namespaced sub-objects for every API
See the REST documentation for the full guide, API reference, and examples.
Installation
# From RubyGems
gem install signalwire-sdk
# Or in your Gemfile
gem 'signalwire-sdk', require: 'signalwire'Published as signalwire-sdk on RubyGems (the bare signalwire name belongs to the unrelated legacy SignalWire Ruby client). The require: hint keeps require 'signalwire' working unchanged.
Requires Ruby >= 3.2.
Documentation
Full reference documentation is available at developer.signalwire.com/sdks/agents-sdk.
Guides are also available in the docs/ directory:
Getting Started
- Agent Guide -- creating agents, prompt configuration, dynamic setup
- Architecture -- SDK architecture and core concepts
- SDK Features -- feature overview, SDK vs raw SWML comparison
Core Features
- SWAIG Reference -- function results, actions, post_data lifecycle
- Contexts and Steps -- structured workflows, navigation, gather mode
- DataMap Guide -- serverless API tools without webhooks
- LLM Parameters -- temperature, top_p, barge confidence tuning
- SWML Service Guide -- low-level construction of SWML documents
Skills and Extensions
- Skills System -- built-in skills and the modular framework
- Third-Party Skills -- creating and publishing custom skills
-
MCP Integration -- connect agents to MCP servers directly (
add_mcp_server/enable_mcp_server)
Deployment
-
CLI Guide --
swaig-testcommand reference - Cloud Functions -- Lambda, Cloud Functions, Azure deployment
- Configuration -- environment variables, SSL, proxy setup
- Security -- authentication and security model
Reference
- API Reference -- complete class and method reference
- Skills Parameter Schema -- skill parameter definitions
Environment Variables
Your Project ID, API Token, and Space come from the SignalWire dashboard: sign in at my.signalwire.com → API (Project ID + Space URL) and API → Tokens (create an API token).
| Variable | Used by | Description |
|---|---|---|
SIGNALWIRE_PROJECT_ID |
RELAY, REST | Project identifier |
SIGNALWIRE_API_TOKEN |
RELAY, REST | API token |
SIGNALWIRE_SPACE |
RELAY, REST | Space hostname (e.g. example.signalwire.com) |
SIGNALWIRE_REST_BASE_URL |
REST | Override the derived REST endpoint (point at a mock/dev server without a code change) |
SIGNALWIRE_REST_CA_FILE |
REST | Path to a custom CA bundle to trust for the REST transport (private-CA deployments) |
SIGNALWIRE_RELAY_CA_FILE |
RELAY | Path to a custom CA bundle to trust for the RELAY WebSocket transport |
SIGNALWIRE_SIGNING_KEY |
Agents | HMAC key for inbound webhook signature validation (validation is off until set) |
SWML_BASIC_AUTH_USER |
Agents | Basic auth username (default: auto-generated) |
SWML_BASIC_AUTH_PASSWORD |
Agents | Basic auth password (default: auto-generated) |
SWML_PROXY_URL_BASE |
Agents | Base URL when behind a reverse proxy |
SWML_DOMAIN |
Agents | External domain used to build absolute webhook/SSL URLs |
SWML_SSL_ENABLED |
Agents | Enable HTTPS (true, 1, yes) |
SWML_SSL_CERT_PATH |
Agents | Path to SSL certificate |
SWML_SSL_KEY_PATH |
Agents | Path to SSL private key |
SWML_ALLOW_PRIVATE_URLS |
Agents | Allow webhook/tool URLs pointing at private/loopback IPs (1/true/yes); off by default (SSRF guard) |
SWML_SKIP_SCHEMA_VALIDATION |
Agents | Skip SWML schema validation (1/true/yes) |
SIGNALWIRE_SKILL_PATHS |
Skills | Extra skill search directories (colon-separated) |
SIGNALWIRE_RELAY_HOST |
RELAY | Override the RELAY WebSocket host (testing / self-hosted) |
SIGNALWIRE_RELAY_SCHEME |
RELAY | Override the RELAY WebSocket scheme (ws/wss) |
SIGNALWIRE_RELAY_CA_FILE |
RELAY | Custom CA bundle for the RELAY TLS (wss://) connection |
SIGNALWIRE_REST_CA_FILE |
REST | Custom CA bundle for the REST HTTPS connection |
SIGNALWIRE_LOG_LEVEL |
All | Logging level (debug, info, warn, error) |
SIGNALWIRE_LOG_MODE |
All | Set to off to suppress all logging |
A ready-to-copy .env.example at the repo root lists every
environment variable the SDK reads, including the skill *_BASE_URL overrides
used for testing.
Testing
Tests, formatting, and linting go through the canonical scripts/run-*.sh
entry points. They self-bootstrap their tool environment (bundle install on a
missing gem) and run from any directory — prefer them over raw rake test /
rubocop.
# Run the full test suite (self-bootstraps, any CWD)
bash scripts/run-tests.sh
# Run a subset — pass a test file (or glob)
bash scripts/run-tests.sh tests/function_result_test.rb
# Coverage
COVERAGE=1 bash scripts/run-tests.sh
# Format (rubocop): apply in place, or --check for verify-only
bash scripts/run-format.sh
bash scripts/run-format.sh --check
# Lint (rubocop, zero offenses); --fix applies safe autocorrect first
bash scripts/run-lint.shLicense
MIT -- see LICENSE for details.