Skip to content

Addresses

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Each Resource is uniquely identified by its Address, allowing for precise targeting and interaction within the Call Fabric ecosystem. This simplifies the development process by providing a standardized way to handle different communication elements, and enhances flexibility, as developers can interact with a wide range of communication tools using a unified approach.

Resources can have multiple addresses, and addresses are mutable. For instance, you can map a SWML script and a Video Room to the same Resource Address. These addresses can be changed or deleted later as needed.

Types

Call Fabric supports multiple address types to accommodate different communication protocols and use cases:

  • Phone numbers: Traditional phone numbers in E.164 format (e.g., +14155551234) that can be mapped to resources for PSTN connectivity.
  • SIP addresses: SIP URIs for VoIP communications (e.g., sip:user@domain.com) enabling direct SIP endpoint connections.
  • Alias: Custom names that provide alternative addressing for resources, making them easier to remember and use (e.g., /support-queue or /main-conference).

Each address type follows the same context and naming conventions described below, allowing seamless integration across different communication channels.

Each Resource Address has two components:

  • Context: Identifies the path of the address. Currently can be public or private.
  • Name: By default the name of the address will be the name of the resource, however, a user can also change or add an alias of an address.

For example, the address for an AI Agent resource named Alice-AI in the public context would be /public/Alice-AI. If you were to change the alias to John-AI the address would become /public/John-AI.

If you are interacting with a resource from within the same context, you can omit the context from the address. For example, if you are interacting with a Subscribers resource named Bob from within the private context, you can use the address /Bob instead of /private/Bob.

Once you have created a Resource, you can use the address to interact with it within the Call Fabric ecosystem. Additionally, you can view the created resource in the Resources tab of the SignalWire Dashboard. Here, you can view the address, type, and other details of the resource.

The Resources page of the SignalWire Dashboard.

The filterable Resources list in the SignalWire Dashboard.


Allow SignalWire IPs through your firewall

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

SignalWire does not publish a static list of IP addresses. Customers should periodically resolve the following domains and update their firewall rules accordingly:

dig sip.signalwire.com | egrep ^sip | awk '{ print $5 }' | xargs -n1 -I{} iptables -A INPUT -s {} -j ACCEPT

dig relay.signalwire.com | egrep ^relay | awk '{ print $5 }' | xargs -n1 -I{} iptables -A INPUT -s {} -j ACCEPT

dig firewall.signalwire.com | egrep ^firewall | awk '{ print $5 }' | xargs -n1 -I{} iptables -A INPUT -s {} -j ACCEPT

Use domain-based authorization rather than hardcoded IPs, addresses are subject to change. Media originates from a wide variety of locations and is non-deterministic.


Calling

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

SignalWire treats all real-time communication channels as part of a unified calling layer. Whether you’re building an AI-powered phone agent, a traditional IVR, a video conferencing app, or a cloud fax workflow, it all runs on the same platform, the same APIs, and the same infrastructure.

AI\ \ Deploy intelligent voice agents powered by LLMs. Build with the Agents SDK, SWML, or the Dashboard, no telephony expertise required. Voice\ \ Make and receive phone calls over PSTN, SIP, and WebRTC. Build IVRs, call routing, recording, and conferencing applications. Video\ \ Add programmable video conferencing to your application with a cloud-based MCU, low latency, and a unified stream for all participants. Fax\ \ Send and receive faxes programmatically with HIPAA-ready, SOC 2 Type II compliant infrastructure.


Chat

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

The SignalWire Chat API is a flexible IP Message Bus that you can use to send and receive messages.

PubSub

The most basic chat functionality is provided in a PubSub environment, which has two main components: Channels and Messages. Messages are published to a channel and the new message event is available to any subscribers. This means the publishers and subscribers are isolated and do not need to know where the messages are coming from or being read. If this sounds like the messaging model that meets your needs, check out our PubSub service.

REST API\ \ Create PubSub Tokens Browser SDK\ \ Integrate PubSub into your web app SDK PubSub Reference\ \ Manage subscriptions and event listeners

Chat

Chat service extends PubSub with a third main component: Members.

You can create channels, members can subscribe to those channels, and they can send attributed messages that every other subscriber will be able to see. Members can even carry state. If this model better suits your needs, Chat is for you!

REST API\ \ Create Chat Tokens Browser SDK\ \ Integrate Chat features into any web app SDK Chat Reference\ \ Manage members, subscriptions and event listeners, and send messages to a particular channel.

Frequently asked questions

Can Chat only send and receive text messages?

Chat can be used to send and receive any JSON-serializable object.

Does Chat support group chats?

Yes! Channels are essentially group chats that members can join and exchange messages in. If you intend to create group chats that use SMS as well, please read the next frequently asked question.

Does Chat support sending/receiving via SMS?

While Chat does not support sending/receiving via SMS by default, using RELAY Realtime SDK along with our Messaging API you can add SMS support (or any other format of communication) to it!

Can Chat messages be logged?

Using the SDK’s Chat API you can listen for the “message” event and log each message accordingly.


HIPAA

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

The Health Insurance Portability and Accountability Act (HIPAA) establishes requirements for protecting Protected Health Information (PHI). As a telecommunications technology company, SignalWire is built to support HIPAA-compliant applications: SignalWire APIs and WebRTC communications services are all encrypted by default via HTTPS, TLS, and/or SRTP/DTLS. When building applications, especially voice AI agents defined with SWML or the Server SDK, that handle healthcare data, you must implement appropriate safeguards to protect PHI throughout the call lifecycle.

This guide provides technical guidance for implementing security controls relevant to HIPAA compliance. It is not legal advice. Healthcare organizations must consult with qualified healthcare compliance experts and legal counsel to ensure their specific implementation meets all applicable HIPAA requirements. Compliance is a shared responsibility between SignalWire, your organization, and your implementation choices.

Overview

HIPAA applies to covered entities and business associates. Individuals, organizations, and agencies that meet the definition of a covered entity must comply with the Rules’ requirements to protect the privacy and security of health information. If a covered entity engages a business associate to help carry out its health care activities, the covered entity must have a written business associate contract that establishes what the business associate has been engaged to do and requires it to comply with the Rules’ requirements to protect PHI.

SignalWire is built to support HIPAA-compliant applications. No PII (Personally Identifiable Information), private resource logs, or other records are publicly accessible, you must have specific access granted by a Space Admin to see logs in the portal, and you must have API credentials to use the API. PII and PHI that may be contained in resources can also be deleted from the logs (message bodies, fax media, message media, and so on). Recordings must be manually enabled, and they can be deleted from the Space altogether or paused during the collection of sensitive data.

What is PHI in voice AI?

In the context of voice AI agents, PHI may include:

  • Patient names, addresses, phone numbers
  • Medical record numbers and account numbers
  • Appointment details and scheduling information
  • Prescription information
  • Lab results and diagnoses
  • Insurance information
  • Any health-related information combined with patient identifiers

Shared responsibility model

LayerResponsibility
SignalWire PlatformInfrastructure security, encryption in transit, secure data centers
Your ApplicationAccess controls, audit logging, PHI handling, prompt design, SWAIG webhook security
Your OrganizationPolicies, training, BAA management, incident response

Business Associate Agreement (BAA)

SignalWire is a Business Associate and offers Business Associate Agreements for healthcare customers. You must have a signed BAA with SignalWire before processing any PHI through the platform.

If you need a BAA signed for HIPAA compliance, reach out to sales@signalwire.com to get started.

Prerequisites

Before deploying a HIPAA-compliant agent, ensure the following are in place.

Infrastructure requirements

RequirementDetails
HTTPSAll endpoints, including your SWAIG and SWML webhooks, must use TLS 1.2 or higher
AuthenticationHTTP Basic Auth required on all webhooks
HostingHIPAA-compliant hosting environment for any service that receives PHI
NetworkFirewall rules limiting access to necessary IPs

HIPAA-compliant providers

When building HIPAA-compliant agents, you must use voice and LLM providers that support BAA coverage. In SWML, voice is selected per language with the voice property of ai.languages, and the model is selected with ai_model in ai.params. The Server SDK exposes the same settings through addLanguage and setParams.

HIPAA-compliant voice providers:

ProviderVoice value
Rimerime.spore
Googlegcloud.en-US-Neural2-F
Amazonpolly.Joanna
Azureazure.en-US-JennyNeural

HIPAA-compliant LLM:

ModelConfiguration
gpt-oss-120bai_model: gpt-oss-120b

Configure the model and a HIPAA-compliant voice as follows:

Server SDK (Python)
Server SDK (TypeScript)
SWML
class HIPAACompliantAgent(AgentBase):
    def __init__(self):
        super().__init__(name="hipaa-agent")

        # Use HIPAA-compliant voice provider
        self.add_language("English", "en-US", "rime.spore")

        # Use HIPAA-compliant LLM
        self.set_params({"ai_model": "gpt-oss-120b"})

Personnel requirements

  • Designated HIPAA Privacy Officer
  • Designated HIPAA Security Officer
  • Workforce training on PHI handling
  • Documented policies and procedures

Access control

When you invite a new user to your SignalWire Space, the admin can specify the projects that they should be allowed to view, limiting accessibility to resource logs to only those who have been granted permission. The API also requires each request to be authenticated with a Space URL, Project ID, and most importantly, an API Token.

We highly recommend that each application that programmatically accesses SignalWire uses its own API tokens, so that you can easily see when these tokens have been used and remove them when necessary. Additionally, take care to make sure only those who need access to a project have it.

The principle of least privilege. The principle of least privilege (POLP) limits users’ access rights to only what is strictly required to do their jobs. Implementing this is a huge help in HIPAA compliance, as well as a solid business practice.

Authentication configuration

Configure strong authentication on the agent’s own endpoints. The Server SDK reads HTTP Basic Auth credentials from environment variables:

# .env file - use strong, unique credentials
<Badge type="tip" text="Fresh" />
SWML_BASIC_AUTH_USER=healthcareagent
SWML_BASIC_AUTH_PASSWORD=your-secure-password-minimum-32-characters

# Never use default or weak passwords in production

If SWML_BASIC_AUTH_PASSWORD is not set, the SDK auto-generates a password on each startup; in production, always set it explicitly.

For sensitive functions, enable token security: mark any PHI-accessing tool secure so its function callback also requires a validated token. Any SWAIG function ( SWML reference, Server SDK guide) that accesses PHI calls out to a web_hook_url you host, in SWML, protect it with HTTP Basic Auth (SWAIG.defaults.web_hook_url as username:password@url) and serve it over HTTPS.

Server SDK (Python)
Server SDK (TypeScript)
from signalwire import AgentBase
from signalwire.core.function_result import FunctionResult

class HealthcareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="healthcare-agent")

        # Mark PHI-accessing functions as secure
        # This adds cryptographic token validation
        self.define_tool(
            name="get_patient_info",
            description="Retrieve patient information by ID",
            parameters={
                "type": "object",
                "properties": {
                    "patient_id": {
                        "type": "string",
                        "description": "Patient identifier"
                    }
                },
                "required": ["patient_id"]
            },
            handler=self.get_patient_info,
            secure=True  # Enables token validation
        )

    def get_patient_info(self, args, raw_data):
        # Validate and sanitize input
        patient_id = args.get("patient_id", "").strip()
        if not self.validate_patient_id(patient_id):
            return FunctionResult("Invalid patient identifier.")

        # Fetch from secure backend
        # ... implementation
        return FunctionResult("Retrieved patient information.")

TLS/HTTPS configuration

Always use HTTPS in production. Configure TLS via environment variables:

# Direct TLS (if not using a reverse proxy)
SWML_SSL_ENABLED=true
SWML_SSL_CERT_PATH=/etc/ssl/certs/healthcare-agent.crt
SWML_SSL_KEY_PATH=/etc/ssl/private/healthcare-agent.key
SWML_SSL_VERIFY_MODE=CERT_REQUIRED

# Enable HSTS
SWML_USE_HSTS=true
SWML_HSTS_MAX_AGE=31536000

For production deployments behind a reverse proxy (nginx, Caddy) that terminates SSL, set the public base URL instead:

SWML_PROXY_URL_BASE=https://healthcare.example.com

Audit logging

HIPAA requires audit trails for PHI access. Write the audit entry in whichever handler backs your function, the tool handler (Server SDK) or your SWAIG web_hook_url handler (SWML), and redact sensitive fields so that PHI never appears in plaintext logs:

Server SDK (Python)
Server SDK (TypeScript)
import logging
from datetime import datetime
from signalwire import AgentBase
from signalwire.core.function_result import FunctionResult

# Configure structured logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
audit_logger = logging.getLogger('hipaa_audit')

class HIPAACompliantAgent(AgentBase):
    def __init__(self):
        super().__init__(name="hipaa-agent")
        self.add_language("English", "en-US", "rime.spore")

    def log_phi_access(self, action, user_id, resource_type, success, details=None):
        """Log PHI access for HIPAA audit trail."""
        audit_logger.info(
            "PHI_ACCESS",
            extra={
                "timestamp": datetime.utcnow().isoformat(),
                "action": action,
                "user_id": user_id,
                "resource_type": resource_type,
                "success": success,
                "details": self.sanitize_for_log(details)
            }
        )

    def sanitize_for_log(self, data):
        """Remove PHI from log entries."""
        if data is None:
            return None

        # Fields to redact
        sensitive_fields = [\
            'ssn', 'social_security',\
            'dob', 'date_of_birth',\
            'address', 'phone',\
            'medical_record', 'mrn',\
            'diagnosis', 'prescription'\
        ]

        sanitized = dict(data) if isinstance(data, dict) else {"value": "[REDACTED]"}
        for field in sensitive_fields:
            if field in sanitized:
                sanitized[field] = "[REDACTED]"

        return sanitized

Required audit log events:

EventRequired fields
Call StartTimestamp, call_id, caller_id (hashed), agent_name
PHI AccessTimestamp, action, resource_type, user_id, success/failure
Function CallTimestamp, function_name, sanitized_args, result_status
AuthenticationTimestamp, auth_type, success/failure, source_ip
Call EndTimestamp, call_id, duration, disposition

Call recording compliance

HIPAA and state laws require consent for call recording. Put the disclosure instruction directly in the AI prompt so it is spoken before any PHI discussion:

Server SDK (Python)
Server SDK (TypeScript)
SWML
class HealthcareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="healthcare-agent")
        self.add_language("English", "en-US", "rime.spore")

        # Add recording disclosure to prompt
        self.prompt_add_section(
            "Recording Disclosure",
            body="""At the start of every call, you MUST inform the caller:

            "This call may be recorded for quality assurance and compliance purposes.
            Your continued participation indicates your consent to recording.
            If you do not wish to be recorded, please let me know now."

            Wait for acknowledgment before proceeding with any PHI discussion."""
        )

Alternatively, you can deliver the disclosure as a static_greeting with static_greeting_no_barge set to true, which forces the entire message to play before the caller can interrupt.

Pausing recording for sensitive data

Pause recording when collecting highly sensitive information. A SWAIG function pauses and resumes the recording, in SWML by returning a SWML action that runs stop_record_call (and later record_call), or in the Server SDK by chaining .stop_record_call() / .record_call() on the FunctionResult:

Server SDK (Python)
Server SDK (TypeScript)
def collect_sensitive_info(self, args, raw_data):
    """Collect sensitive info with recording paused."""
    return (
        FunctionResult(
            "I'll need to collect some sensitive information. "
            "Recording will be paused during this process."
        )
        .stop_record_call(control_id="main_recording")
        # Recording is now paused
        # Collect sensitive data via subsequent function calls
    )

def resume_after_sensitive(self, args, raw_data):
    """Resume recording after sensitive collection."""
    return (
        FunctionResult("Thank you. Resuming our conversation.")
        .record_call(control_id="main_recording", stereo=True)
    )

Recording retention

Call recordings containing PHI are considered part of the medical record. HIPAA does not specify a retention period for PHI itself, retention requirements come from state medical records laws, other federal regulations (for example, Medicare conditions of participation), and organizational policies.

The HIPAA six-year retention requirement (45 CFR 164.530(j)) applies to administrative documentation, policies, procedures, training records, risk assessments, and audit logs, not to PHI or call recordings.

ConsiderationRecommendation
Retention PeriodFollow state medical records laws and organizational policy (commonly 7-10 years for adults; longer for minors)
StorageEncrypted at rest, access-controlled
DestructionSecure deletion with audit trail when retention period expires
Access LoggingLog all recording access

Manage recordings through the REST API, list them to audit access, and delete them once their retention period expires.

List recordings\ \ Retrieve your call recordings via the REST API. Delete a recording\ \ Permanently delete a recording when its retention period expires.

PHI handling best practices

Keeping PHI out of the LLM with global_data and meta_data

One of the most effective ways to protect PHI is to keep it away from the LLM entirely. Use global_data or meta_data to store sensitive information while keeping it out of the model’s context.

  • ai.global_data, a key-value object that persists for the whole AI session. It is available to your prompts, AI params, and the SWML returned by SWAIG functions, and is updated at runtime with the set_global_data action (or update_global_data on a FunctionResult). As long as you never template a value into the prompt or a spoken response, it stays out of the model’s context.
  • meta_data, an environmental variable scoped locally to a function (and its web_hook_url), updated with the set_meta_data action (see the set_meta_data guide). Use it for per-function context that no other function, and never the model’s conversation, should see.
Data locationLLM sees itYour function handler can use it
Prompt / conversation / sayYesYes
Function arguments and responsesYesYes
global_data (not templated into the prompt)NoYes
meta_dataNoYes (locally to the function)

A verification function stores the resolved patient record in global_data and returns only a generic confirmation, so the identifiers your other functions rely on are never exposed to the model:

Server SDK (Python)
Server SDK (TypeScript)
class CompartmentalizedHealthcareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="compartmentalized-healthcare")
        self.add_language("English", "en-US", "rime.spore")

    def verify_patient(self, args, raw_data):
        """Verify patient and store PHI in global_data, not in AI context."""
        dob = args.get("date_of_birth")
        ssn_last4 = args.get("ssn_last4")

        # Fetch full patient record from database
        patient = self.lookup_patient(dob, ssn_last4)

        if patient:
            # Store sensitive data in global_data - LLM cannot see this
            # Only your SWAIG functions can access it
            return (
                FunctionResult("I've verified your identity. How can I help?")
                .update_global_data({
                    "patient_id": patient["id"],
                    "mrn": patient["medical_record_number"],
                    "full_name": patient["full_name"],
                    "ssn": patient["ssn"],
                    "insurance_id": patient["insurance_id"],
                    "verified": True
                })
            )

        return FunctionResult("I couldn't verify your identity.")

    def schedule_appointment(self, args, raw_data):
        """Use stored PHI without exposing it to the LLM."""
        # Retrieve PHI from global_data - never sent to LLM
        global_data = raw_data.get("global_data", {})

        if not global_data.get("verified"):
            return FunctionResult("Please verify your identity first.")

        # Use the stored patient_id and MRN for scheduling
        patient_id = global_data.get("patient_id")
        mrn = global_data.get("mrn")

        # Schedule using real identifiers, but LLM only knows
        # "the verified patient" - not the actual PHI
        appointment = self.create_appointment(patient_id, args.get("date"))

        # Return confirmation without exposing PHI to AI
        return FunctionResult(
            f"Your appointment is confirmed for {args.get('date')}. "
            "You'll receive a confirmation at the contact information on file."
        )

A later function, for example, scheduling an appointment, reads ${global_data.patient_id} and ${global_data.mrn} on the server side and confirms the booking without ever speaking the identifiers back.

Collecting PHI via IVR (bypassing the LLM)

For maximum PHI protection, collect sensitive values with the prompt verb (DTMF or speech) instead of through conversation. Input gathered this way is placed in the prompt_value variable for your SWML and webhooks, it never enters the model’s context or the transcript, and with recording paused it never reaches the recording either:

Server SDK (Python)
Server SDK (TypeScript)
SWML
from signalwire import AgentBase, FunctionResult

agent = AgentBase(name="healthcare-agent", route="/agent")

@agent.tool(name="collect_dob", description="Collect date of birth via DTMF, bypassing the LLM")
def collect_dob(args, raw_data=None):
    # Run the SWML `prompt` verb to collect digits. The input lands in ${prompt_value}
    # and never enters the model's context, the transcript, or (with recording paused)
    # the recording.
    return (
        FunctionResult()
        .execute_swml({
            "version": "1.0.0",
            "sections": {
                "main": [\
                    {"stop_record_call": {"control_id": "main_recording"}},\
                    {"prompt": {\
                        "play": "say:Please enter your 8-digit date of birth. For example, 0 3 1 5 1 9 8 5 for March 15, 1985.",\
                        "max_digits": 8,\
                        "digit_timeout": 15,\
                    }},\
                    {"record_call": {"control_id": "main_recording", "stereo": True}},\
                ]
            },
        })
        .set_response(
            "Thank the caller for providing their information and continue verifying "
            "their identity. Never repeat sensitive values back to the caller."
        )
    )

agent.run()

This approach provides several layers of protection:

Collection methodLLM sees dataTranscript containsRecording contains
Normal conversationYesYesYes
DTMF via promptNoNoTones only
Speech via promptNoNoYes (pause recording to exclude)
global_data / meta_dataNoNoNo

Combined pattern: maximum PHI protection

For the strongest protection, combine paused recording with DTMF collection of every sensitive field in a single flow, then resume recording and hand control back to the AI with only a generic confirmation:

YAMLJSON

version: 1.0.0
sections:
  main:
    # Pause recording for the entire sensitive collection
    - stop_record_call:
        control_id: main_recording
    # Collect date of birth via DTMF - bypasses the LLM
    - prompt:
        play: "say:Please enter your 8-digit date of birth. For example, 0 3 1 5 1 9 8 5 for March 15, 1985."
        max_digits: 8
        digit_timeout: 15
    - set:
        dob: "${prompt_value}"
    # Collect last 4 of SSN via DTMF - bypasses the LLM
    - prompt:
        play: "say:Please enter the last 4 digits of your Social Security Number."
        max_digits: 4
        digit_timeout: 10
    - set:
        ssn_last4: "${prompt_value}"
    # Resume recording, then return to the AI with a generic confirmation
    - record_call:
        control_id: main_recording
        stereo: true
    - ai:
        prompt:
          text: |
            Thank the caller for providing their information and confirm you are now
            verifying their identity. Never repeat the collected values back.

Prompt design for PHI protection

Design prompts to minimize PHI exposure. Whether you write a single text prompt or a structured POM, encode the protection and verification rules explicitly. In the Server SDK, promptAddSection builds the POM for you:

Server SDK (Python)
Server SDK (TypeScript)
SWML
class SecureHealthcareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="secure-healthcare")
        self.add_language("English", "en-US", "rime.spore")

        self.prompt_add_section(
            "PHI Protection Rules",
            body="""CRITICAL: Follow these rules for Protected Health Information:

            1. NEVER repeat back sensitive information like:
               - Social Security Numbers
               - Full date of birth
               - Medical record numbers
               - Detailed diagnosis information

            2. When confirming patient identity, use partial information:
               - "I see your date of birth ends in [last 2 digits of year]"
               - "Your record shows an address on [street name only]"

            3. NEVER include PHI in error messages or clarifications

            4. If asked to repeat sensitive info, say:
               "For security purposes, I cannot repeat that information."

            5. Verify caller identity before ANY PHI access using
               established verification procedures."""
        )

        self.prompt_add_section(
            "Identity Verification",
            body="""Before accessing ANY patient information:

            1. Collect patient identifier (MRN or account number)
            2. Verify with TWO of the following:
               - Date of birth
               - Last 4 of SSN
               - Address on file
               - Phone number on file

            Only proceed after successful verification."""
        )

Secure function design

Design SWAIG functions to return the minimum necessary PHI. The handler should verify the caller is authorized, log the access, and respond with only what the caller needs to hear, never provider names or detailed locations. On an authorization failure, return a non-revealing message:

Server SDK (Python)
Server SDK (TypeScript)
def check_appointment(self, args, raw_data):
    """Check appointment - returns minimal PHI."""
    patient_id = args.get("patient_id")

    # Verify caller is authorized
    if not self.verify_caller_authorization(raw_data, patient_id):
        self.log_phi_access("appointment_check", patient_id, "appointment", False)
        return FunctionResult(
            "I was unable to verify your identity. "
            "Please contact us directly for assistance."
        )

    # Log the access
    self.log_phi_access("appointment_check", patient_id, "appointment", True)

    # Return minimal information
    appointment = self.get_next_appointment(patient_id)
    if appointment:
        # Don't include provider names or detailed location
        return FunctionResult(
            f"Your next appointment is on {appointment['date']} "
            f"at {appointment['time']}. "
            f"Please arrive 15 minutes early."
        )

    return FunctionResult("You have no upcoming appointments scheduled.")

Input validation

Validate all inputs to prevent injection and ensure data integrity. Declare expected inputs and constraints in the function parameters ( SWML reference, Server SDK guide) so the model can only supply well-formed values, then re-validate in the handler:

Server SDK (Python)
Server SDK (TypeScript)
SWML
import re

def validate_patient_id(self, patient_id):
    """Validate patient ID format."""
    # Only allow alphanumeric, specific length
    if not patient_id:
        return False
    if not re.match(r'^[A-Z0-9]{8,12}$', patient_id):
        return False
    return True

def validate_date_of_birth(self, dob):
    """Validate DOB format."""
    try:
        # Expected format: MM/DD/YYYY
        if not re.match(r'^\d{2}/\d{2}/\d{4}$', dob):
            return False
        # Additional validation...
        return True
    except Exception:
        return False

Redacting stored message media

To remove PHI from message logs after the fact, redact the body of a previously sent message through the REST API. Redaction clears the message body for compliance or privacy while keeping a record that the message was sent; the original body is overwritten and cannot be recovered. Only messages in a terminal state (delivered, undelivered, failed) are eligible.

Redact a message\ \ Clear the body of a previously sent message via the REST API. List message logs\ \ Find the message IDs whose bodies you need to redact.

Deployment

The PHI-handling parts of an agent live in the services you host: the endpoint that serves the SWML (or the Server SDK app) and the web_hook_url endpoints that back your SWAIG functions. Secure those services regardless of how you deploy them.

Serverless deployment

For AWS Lambda, Google Cloud Functions, or Azure Functions hosting your agent or SWAIG webhooks:

ConsiderationImplementation
Secrets ManagementUse AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault for SWAIG credentials
VPC ConfigurationDeploy in a VPC with no public internet access where possible
IAM PoliciesLeast-privilege access to resources
LoggingCloudWatch/Cloud Logging with encryption
Cold StartsMay affect audit log timing; account for this

The Server SDK serves serverless platforms through handle_serverless_request, with a mode for each provider:

# handler.py - Serverless with HIPAA considerations
import os
from signalwire import AgentBase

class HIPAAServerlessAgent(AgentBase):
    def __init__(self):
        super().__init__(name="hipaa-serverless")
        self.add_language("English", "en-US", "rime.spore")

        # Ensure auth is configured
        if not os.environ.get("SWML_BASIC_AUTH_PASSWORD"):
            raise ValueError("SWML_BASIC_AUTH_PASSWORD must be set")

agent = HIPAAServerlessAgent()

def lambda_handler(event, context):
    """AWS Lambda entry point."""
    return agent.handle_serverless_request(event, context, mode="lambda")

Self-hosted deployment

Self-hosted security checklist:

  • Deploy in HIPAA-compliant data center or cloud region
  • Enable full-disk encryption on all servers
  • Configure host-based firewall
  • Implement intrusion detection
  • Enable comprehensive system logging
  • Regular security patching schedule
  • Network segmentation from other workloads

A container deployment that keeps the agent off the public internet and terminates TLS at a reverse proxy:

# docker-compose.yml for a HIPAA-compliant deployment
version: '3.8'

services:
  healthcare-agent:
    build: .
    environment:
      - SWML_BASIC_AUTH_USER=${SWML_BASIC_AUTH_USER}
      - SWML_BASIC_AUTH_PASSWORD=${SWML_BASIC_AUTH_PASSWORD}
      - SWML_PROXY_URL_BASE=${SWML_PROXY_URL_BASE}
      - SIGNALWIRE_LOG_LEVEL=info
    networks:
      - internal
    deploy:
      resources:
        limits:
          memory: 512M

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/ssl/certs:ro
    depends_on:
      - healthcare-agent
    networks:
      - internal
      - external

networks:
  internal:
    internal: true  # No external access
  external:

Administrative safeguards

Access management

Server SDK (Python)
Server SDK (TypeScript)
# Example: Role-based function access
class RoleBasedHealthcareAgent(AgentBase):

    # Define role permissions
    ROLE_PERMISSIONS = {
        "patient": ["check_appointment", "request_prescription_refill"],
        "staff": ["check_appointment", "schedule_appointment", "view_patient_summary"],
        "provider": ["check_appointment", "schedule_appointment", "view_patient_summary",\
                     "view_medical_record", "add_clinical_note"]
    }

    def __init__(self):
        super().__init__(name="role-based-healthcare")
        self.caller_role = None

    def verify_permission(self, function_name):
        """Check if current caller role has permission."""
        if not self.caller_role:
            return False
        allowed = self.ROLE_PERMISSIONS.get(self.caller_role, [])
        return function_name in allowed

Incident response

For a security incident, have your handlers refuse PHI access and return a safe fallback when an emergency flag is set. Check an AGENT_EMERGENCY_MODE flag and short-circuit before any PHI access:

Server SDK (Python)
Server SDK (TypeScript)
import os

class HealthcareAgent(AgentBase):
    def __init__(self):
        super().__init__(name="healthcare-agent")
        self.emergency_mode = os.environ.get("AGENT_EMERGENCY_MODE", "").lower() == "true"

    def access_phi(self, args, raw_data):
        """PHI access with emergency mode check."""
        if self.emergency_mode:
            audit_logger.warning("PHI_ACCESS_BLOCKED_EMERGENCY_MODE")
            return FunctionResult(
                "This service is temporarily unavailable. "
                "Please call our main line for assistance."
            )

        # Normal PHI access logic...

Incident response requirements:

RequirementDetails
Breach NotificationNotify HHS under the HHS Breach Notification Rule, within 60 days for breaches affecting 500+ individuals; smaller breaches in the annual report
Patient NotificationNotify affected individuals without unreasonable delay, no later than 60 days after discovery
DocumentationDocument all incidents and response actions
Root Cause AnalysisInvestigate and remediate vulnerabilities

Compliance checklist

Pre-deployment

  • Signed BAA with SignalWire
  • HIPAA-compliant hosting environment selected
  • Strong authentication credentials configured
  • TLS/HTTPS properly configured
  • Audit logging implemented and tested
  • PHI handling procedures documented
  • Workforce training completed
  • Incident response plan documented
  • Recording consent disclosures implemented
  • Data retention policies defined

Ongoing compliance

  • Regular access reviews (quarterly recommended)
  • Audit log reviews (monthly recommended)
  • Security patch management
  • Annual risk assessment
  • Annual HIPAA training refresh
  • BAA review and updates as needed
  • Penetration testing (annual recommended)
  • Business continuity testing

Documentation requirements

Maintain documentation for:

DocumentRetention
Policies and Procedures6 years from creation or last effective date
Risk Assessments6 years
Training Records6 years from training date
BAAs6 years from termination
Audit Logs6 years
Incident Reports6 years

Complete example

Complete examples of a HIPAA-compliant healthcare appointment agent in the Server SDKs and SWML are presented in the tabs below.

Server SDK (Python)
Server SDK (TypeScript)
SWML
#!/usr/bin/env python3
"""HIPAA-compliant healthcare appointment agent."""

import os
import logging
from datetime import datetime
from signalwire import AgentBase
from signalwire.core.function_result import FunctionResult

# Audit logging setup
audit_logger = logging.getLogger('hipaa_audit')
audit_logger.setLevel(logging.INFO)

class HIPAAHealthcareAgent(AgentBase):
    """Healthcare agent with HIPAA compliance controls."""

    def __init__(self):
        super().__init__(name="hipaa-healthcare-agent")

        # Use HIPAA-compliant voice and LLM providers
        self.add_language("English", "en-US", "rime.spore")
        self.set_params({"ai_model": "gpt-oss-120b"})

        # Check required security configuration
        if not os.environ.get("SWML_BASIC_AUTH_PASSWORD"):
            raise ValueError("SWML_BASIC_AUTH_PASSWORD required for HIPAA compliance")

        self.emergency_mode = os.environ.get("AGENT_EMERGENCY_MODE", "").lower() == "true"
        self.verified_caller = False

        self._configure_prompts()
        self._setup_tools()

    def _configure_prompts(self):
        """Configure HIPAA-compliant prompts."""
        self.prompt_add_section(
            "Role",
            body="You are a healthcare appointment assistant for Example Medical Center."
        )

        self.prompt_add_section(
            "Recording Disclosure",
            body="""At the START of every call, say:

            "Thank you for calling Example Medical Center. This call may be recorded
            for quality and compliance purposes. How may I help you today?"
            """
        )

        self.prompt_add_section(
            "PHI Protection",
            body="""CRITICAL RULES:
            1. Verify caller identity before accessing ANY patient information
            2. Never repeat back full SSN, DOB, or medical record numbers
            3. Use partial confirmation only ("ending in...", "on file as...")
            4. Never include PHI in error messages
            5. If unsure about authorization, do not provide information
            """
        )

        self.prompt_add_section(
            "Verification Process",
            body="""Before accessing patient information:
            1. Ask for patient's date of birth
            2. Ask for last 4 digits of SSN OR phone number on file
            3. Use verify_patient function to confirm
            4. Only proceed if verification succeeds
            """
        )

    def _setup_tools(self):
        """Configure secure tools."""
        self.define_tool(
            name="verify_patient",
            description="Verify patient identity before accessing records",
            parameters={
                "type": "object",
                "properties": {
                    "date_of_birth": {
                        "type": "string",
                        "description": "Patient date of birth (MM/DD/YYYY)"
                    },
                    "verification_value": {
                        "type": "string",
                        "description": "Last 4 of SSN or phone number"
                    }
                },
                "required": ["date_of_birth", "verification_value"]
            },
            handler=self.verify_patient,
            secure=True
        )

        self.define_tool(
            name="check_appointments",
            description="Check patient appointments (requires prior verification)",
            parameters={
                "type": "object",
                "properties": {}
            },
            handler=self.check_appointments,
            secure=True
        )

    def _log_audit(self, action, success, details=None):
        """Log HIPAA audit event."""
        audit_logger.info(
            f"HIPAA_AUDIT|{datetime.utcnow().isoformat()}|{action}|"
            f"{'SUCCESS' if success else 'FAILURE'}|{details or ''}"
        )

    def verify_patient(self, args, raw_data):
        """Verify patient identity."""
        if self.emergency_mode:
            self._log_audit("verify_patient", False, "emergency_mode")
            return FunctionResult(
                "Our system is temporarily unavailable. Please call back later."
            )

        dob = args.get("date_of_birth", "")
        verification = args.get("verification_value", "")

        # In production, verify against your patient database
        # This is a placeholder for the actual verification logic
        verified = self._verify_against_database(dob, verification)

        if verified:
            self.verified_caller = True
            self._log_audit("verify_patient", True, "identity_confirmed")
            return FunctionResult(
                "Thank you, I've verified your identity. How can I help you today?"
            )
        else:
            self._log_audit("verify_patient", False, "verification_failed")
            return FunctionResult(
                "I wasn't able to verify your identity with the information provided. "
                "Please contact our office directly for assistance."
            )

    def check_appointments(self, args, raw_data):
        """Check appointments - requires prior verification."""
        if not self.verified_caller:
            self._log_audit("check_appointments", False, "not_verified")
            return FunctionResult(
                "I need to verify your identity first. "
                "Can you provide your date of birth?"
            )

        if self.emergency_mode:
            self._log_audit("check_appointments", False, "emergency_mode")
            return FunctionResult(
                "Our scheduling system is temporarily unavailable."
            )

        # Fetch appointments from your system
        # Return minimal necessary information
        self._log_audit("check_appointments", True, "appointments_retrieved")
        return FunctionResult(
            "Your next appointment is scheduled for Monday at 2:30 PM "
            "with the medical team. Please arrive 15 minutes early."
        )

    def _verify_against_database(self, dob, verification):
        """Verify patient against database. Implement your logic here."""
        # Placeholder - implement actual database verification
        return True

if __name__ == "__main__":
    agent = HIPAAHealthcareAgent()
    agent.run()

Resources

SignalWire resources:

  • Contact SignalWire for a BAA: sales@signalwire.com
  • SWML AI reference
  • SWAIG functions reference
  • Server SDK, building AI agents guide
  • Server SDK, defining functions guide
  • Server SDK, AgentBase reference (Python)
  • Server SDK, AgentBase reference (TypeScript)

HIPAA resources:

  • HHS HIPAA for professionals
  • HHS HIPAA Security Rule guidance
  • NIST SP 800-66 Rev. 2, Implementing the HIPAA Security Rule
  • HHS Breach Notification Rule

Summary

Building HIPAA-compliant communications and voice AI applications requires attention to:

  1. Technical controls, authenticated, HTTPS SWAIG webhooks; encryption; audit logging in your handlers.
  2. Administrative controls, policies, training, role-based access management.
  3. PHI handling, minimization, identity verification, and keeping PHI out of the model with global_data, meta_data, and DTMF collection.
  4. Documentation, policies, audit trails, incident records.

SignalWire is built to support HIPAA-compliant applications, and provides the building blocks, AI prompts and post-prompts, AI-inaccessible data stores, recording control, and secure SWAIG functions, needed for compliance. Your responsibility is to configure these features appropriately, secure the webhooks that handle PHI, and maintain the administrative safeguards required by HIPAA. Always consult with qualified healthcare compliance experts and legal counsel to ensure your specific implementation meets all applicable requirements.


E911

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

What is E911?

E911, or Enhanced 911, is a support system for wireless and VoIP phone users who dial 911, the standard number for requesting help in an emergency across supported countries. Since wireless users are often mobile, this enhancement helps the 911 service by allowing the location of the user to be reported to the call receiver.

SignalWire Addresses allows for a collection of addresses in your SignalWire Space so that they can be utilized for uses like E911 or phone number locality requirements for certain countries around the world.

E911 requires a support request

By default, SignalWire phone numbers purchased through the Dashboard don’t support E911. To enable E911 services, contact the SignalWire Support Team by creating a support ticket in your Dashboard.

Testing E911

There is a proper way to test E911 for your SignalWire number. It’s best to arrange a test call to 911 services and verify the E911 information pops up on their screen correctly. You can call your local 911 center non-emergency number, and notify them you’re testing a VOIP system for the correct display of location information. You should be able to get the non-emergency phone number from local law enforcement or an internet search. All 911 centers have a non-emergency number for testing purposes.

“How do I place a “test” call to make sure 911 works for me?”

“Test calls confirm that your local 911 service can receive your 911 call and has the correct location information. Test calls can be scheduled by contacting your local 911 call center via its non-emergency phone number. To contact the local 911 center responsible for answering calls from your location, go here and click on the state in which you are located. The person responsible for operating the state’s 911 system will be identified, and they should know who you should talk to at your local 911 call center, to schedule a day and time for test calls.”

As stated before, SignalWire phone numbers purchased through the Dashboard don’t support E911. If you’d like to have E911 services enabled, please contact the SignalWire Support Team. Once E911 is enabled for the number by working with the SignalWire support team, you can follow these steps to set up E911 and make a test call:

  1. Complete the Set of Instructions from the Previous Section.
  2. Wait about 30 minutes for SignalWire to properly set up E911 on your line.
  3. Do a test call to 933 first, and listen to your E911 info playback. If you hear the automated information, you’re ready to do a real test.If you hear an error tone, wait a little longer, then try again.
  4. Call your local police department’s non-emergency number. Let them know you’re making a VoIP test call to verify location information.
  5. Once you’ve notified your local emergency center of an incoming test call, make the real call to 911 and verify your address displays correctly with the answering personnel.

Provided all the information displays correctly, your verification process for E911 services is complete. The carriers validate the address. If it passes, you’re good to go.


FusionPBX

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

FusionPBX is a FreeSWITCH-based multi-tenant PBX that provides a robust set of features for business phone systems. Using SignalWire services with FusionPBX allows you to leverage our high call quality and low rates.

Creating a SIP Endpoint

If you don’t have a SIP endpoint set up already, the first step is to create a SIP Endpoint via the SignalWire Dashboard and connect it to a phone number.

FusionPBX GUI Set Up

From your FusionPBX installation’s web GUI go to Accounts > Gateways.

The Gateways item under Accounts.

The Gateways item under Accounts.

This is where you will use the credentials you created in the first step. Once you are in the Gateways section, click the ADD button in the top right corner.

Selecting the Add button in the Gateways section.

Selecting the Add button in the Gateways section.

Enter the following information in the Gateway form:

Gateway: This is a label. In this example we will define this as signalwire

Username: Your username as created in the SIP endpoint on SignalWire

Password: The password you set above

Proxy: YOURSPACE-SIPDOMAIN.sip.signalwire.com (Replace with your proxy address)

Register: True

The Proxy address and other SIP credentials can be found in the SignalWire Dashboard by going to the SIP > SIP Settings tab as shown below.

Selecting the SIP Settings pane of the SIP tab.

Selecting the SIP Settings pane of the SIP tab.

Click the Advanced button to expand additional settings

  1. Register Transport: TLS (This can also be TCP but is better to be secure with TLS)
  2. Ping: 90
  3. Profile: internal
  4. Enabled: True Click save at the top right corner.

Your form should look similar to the following.

In order to use TLS on your FusionPBX installation, you must have SSL certificates like Let’s Encrypt or others configured. You can find this detailed further in the FusionPBX member documentation.

A screenshot of the Gateway form. The Gateway name is set to signalwire. Username, password, and proxy are filled in with appropriate values. Expire seconds is set to 800. Register is set to True. Retry Seconds is set to 30. Under Advanced, the Context is set to Public and the Profile is set to Internal.

A screenshot of the Gateway form. The Gateway name is set to signalwire. Username, password, and proxy are filled in with appropriate values. Expire seconds is set to 800. Register is set to True. Retry Seconds is set to 30. Under Advanced, the Context is set to Public and the Profile is set to Internal.

You should now have a gateway registered(REGED) as shown in the image below! If you don’t see it yet, try clicking Start.

A screenshot of a table with gateways. The example gateway reads 'REGED' in the State column.

A screenshot of a table with gateways. The example gateway reads 'REGED' in the State column.

Making Outbound Calls over SignalWire

After the gateway is configured in your FusionPBX installation and registered, we can now configure an outbound route! Go to Dialplan > Outbound Routes and click the ADD button at the top right.

A screenshot of the Dialplan menu, with Outbound Routes selected.

A screenshot of the Dialplan menu, with Outbound Routes selected.

Selecting the Add button.

Selecting the Add button.

Fill in the form with a few simple settings:

  1. Gateway: Select signalwire from the drop down list
  2. Dialplan Expression: Choose North America (This can be typed in manually or you can pick other areas also)

Check your form is similar to the following, and click Save.

Settings in the Outbound Routes form.

Settings in the Outbound Routes form.

Recieving Inbound Calls over SignalWire

Go to Accounts > Extensions. Here you can create an extension. This is needed to point your registered peer to so you can receive calls.

Selecting the Extensions item in the Accounts menu.

Selecting the Extensions item in the Accounts menu.

Create an extension by clicking the Add button at the top right. In this example, we will create extension 900 and click Save at the top right.

Go to Dialplan > Destinations from your FusionPBX installation. We will now create an inbound route to connect the SignalWire peer and the extension you just created.

Selecting the Destinations item in the Dialplan menu.

Selecting the Destinations item in the Dialplan menu.

Create a destination by clicking the +ADD button at the top right.

Selecting the Add button.

Selecting the Add button.

Fill in the form with the necessary information

  1. Destination: Your SignalWire phone number, with the +1 (eg. +15556677888)
  2. Caller ID Name: This is optional (also see the documentation for Caller ID settings on SignalWire)
  3. Caller ID Number: This is optional
  4. Actions: 900 (This is where the call will go to). The actions can be an extension, IVR, Time condition, etc.

Click the Save button at the top right, after checking your form looks similar to the following.

Form settings.

Form settings.

This next step is VERY important. This must be set in order to receive calls to your FusionPBX installation. In the menu, look for Advanced > Default Settings and click the Add button on the top right.

Selecting the Add button under Default Settings.

Selecting the Add button under Default Settings.

In the form, enter the following information:

  1. Category: dialplan
  2. Subcategory: destination
  3. Type: text
  4. Value: ${sip_to_user}
  5. Enabled: True
  6. Description: optional

Check the form looks similar to the following, then click Save. After saving, click the top right Reload button.

Selecting the Save button.

Selecting the Save button.

Access Control

The IPs are needed to make/receive calls, but they can change frequently. You can find the IPs by querying sip.signalwire.com. CIDR is used so IPs must be added with a x.x.x.x/32.

Windows

nslookup sip.signalwire.com

Mac/linux

dig sip.signalwire.com

You can add these in Advanced, > Access Controls, > Domains.

A screenshot of the Access Control page, with a list of nodes.

A screenshot of the Access Control page, with a list of nodes.

fail2ban

If you are using fail2ban to protect FreeSWITCH, you can add SignalWire IPs directly to iptables via the linux command line!

Check out our article on how to allow SignalWire IPs through your firewall to learn more.


Get started

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

SignalWire is a programmable unified communications platform that unifies voice, messaging, video, and AI into a single control plane. SignalWire’s APIs and SDKs enable developers to build state-of-the-art realtime communication experiences without needing to manage complex telecom infrastructure or stitch together disconnected tools.

This guide will help you set up your account, explore the core APIs, and launch your first application.

Your SignalWire Space

When you create a SignalWire account, you also create a Space, like spacename.signalwire.com. Your Dashboard is located at that subdomain. In your Dashboard, you can:

  • Buy and configure phone numbers
  • Create and manage your applications
  • View call logs and analytics
  • Access your API credentials
  • Set up AI agents, call flows, and more

The SignalWire Dashboard

Start building

Let’s begin by figuring out the best way for you to build. The right approach depends on what you’re creating and how you prefer to work.

What are you trying to build?

AI Application

You want to build an AI-powered voice agent that handles phone calls.

This is common for:

  • Automated customer service
  • Appointment scheduling and reminders
  • FAQ bots and information lines
  • Lead qualification and surveys
  • Virtual receptionists

Agents SDK - A Python framework for building sophisticated AI voice agents. You get full control over prompts, custom functions (SWAIG) dedicated to AI, customizeable conversation flow, and seamless LLM integration. Best for complex agents that need to perform actions like booking appointments, looking up data, or integrating with your systems.

AI Agent (Dashboard) - Configure an AI agent directly in your Dashboard without writing code. Set up prompts, choose a voice, add functions, and connect it to a phone number. Great for getting started quickly or simpler use cases.

SWML with AI - Add AI capabilities to your SWML scripts using the ai method. Good when you want AI as part of a larger call flow that includes other logic.

Agents SDK\ \ Python, full control SWML AI Method\ \ AI in call flows Dashboard AI Agent\ \ No-code setup

Quick start with Agents SDK:

pip install signalwire-sdk
from signalwire import AgentBase

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(name="Assistant", route="/agent")
        self.prompt_add_section("main", body="You are a helpful assistant for Acme Corp.")

agent = MyAgent()

if __name__ == "__main__":
    agent.run()

Agents SDK Quickstart\ \ Build your first AI voice agent

Browser or Mobile App

You want voice, video, or chat directly in a web browser or mobile app.

This is common for:

  • Click-to-call buttons on websites
  • In-app voice or video calling
  • Browser-based contact centers
  • Video conferencing applications

Browser SDK - Our JavaScript SDK for building custom WebRTC experiences. You get full control over the UI and user experience. Best when you need video conferencing, custom calling interfaces, or real-time chat.

Browser SDK Guide\ \ Build voice, video, and chat in the browser

Server Application

You’re building a backend service that handles calls or messages.

This is common for:

  • IVR systems and phone menus
  • Automated call routing
  • SMS notifications and two-factor auth
  • Call centers and support systems
  • AI voice agents

Do you need real-time control?

If you need simple call handling (IVRs, call forwarding, playing messages), use SWML. Your server responds to webhooks with JSON/YAML instructions. It’s stateless, works with any programming language, and is the simplest approach for most use cases.

If you need real-time control (live call monitoring, mid-call transfers, complex orchestration), use the Realtime SDK. It maintains a persistent WebSocket connection for instant, bi-directional communication. Best for applications that need to react to events as they happen.

If you’re building AI voice agents, use the Agents SDK. It’s a Python framework specifically designed for creating conversational AI that handles phone calls. It handles the complexity of integrating with LLMs and managing conversations.

Agents SDK\ \ AI voice agents, Python SWML\ \ HTTP webhooks, any language Realtime SDK\ \ WebSocket, Node.js

No-Code / Low-Code

You want to build without writing much (or any) code.

This is common for quick prototypes, simple IVRs, and small businesses needing basic call handling.

Call Flow Builder - A visual, drag-and-drop interface for building call handling logic. No code required. You connect nodes to define what happens when someone calls - play a message, gather input, route to different people, etc.

SWML Scripts - Write simple JSON or YAML scripts directly in your Dashboard. It’s not quite “no code” but it’s very low code, and you don’t need to run any servers. SignalWire hosts the scripts for you.

AI Agent - Configure a conversational AI agent through your Dashboard. Set up prompts, choose a voice, and connect it to a phone number. The AI handles conversations autonomously.

Call Flow Builder\ \ Visual drag-and-drop SWML Scripts\ \ Simple JSON/YAML AI Agent\ \ Dashboard AI setup

Quick start for no-code:

  1. Buy a phone number - Go to Phone Numbers in your Dashboard
  2. Create a Call Flow - Use Call Flow Builder to design what happens when someone calls
  3. Assign it to your number - Edit the number settings and select your Call Flow
  4. Call your number - Test it out!
Migrating from Twilio*

You have an existing Twilio application and want to move to SignalWire.

Good news, SignalWire’s Compatibility API is designed as a drop-in replacement. In most cases, you can switch by changing a few lines of code.

What’s compatible:

TwilioSignalWire
TwiMLcXML (same syntax)
REST APICompatibility REST API
Helper LibrariesCompatibility SDKs (Node, Python, Ruby, C#)
Account SIDProject ID
Auth TokenAPI Token

AI is not supported in the Compatibility API. Check out the AI Application section instead.

Migration steps:

  1. Create a SignalWire account at signalwire.com/signup
  2. Get your credentials from Dashboard > API > API Tokens
  3. Update your code to use SignalWire’s SDK and credentials
  4. Update webhook URLs if needed (cXML syntax is identical to TwiML)
  5. Buy or port phone numbers to SignalWire
  6. Test your application
// Change from this (Twilio)
const twilio = require("twilio");
const client = twilio(ACCOUNT_SID, AUTH_TOKEN);

// To this (SignalWire)
const { RestClient } = require("@signalwire/compatibility-api");
const client = RestClient(PROJECT_ID, API_TOKEN, {
  signalwireSpaceUrl: "your-space.signalwire.com"
});

Compatibility API Guide\ \ Complete migration documentation


Core concepts

Communication channels

SignalWire supports the following communication channels:

Voice\ \ Phone calls, IVRs, recording, conferencing Video\ \ Video rooms, screen sharing, recordings Messaging\ \ SMS and MMS text messages Chat\ \ Real-time chat for web and mobile apps AI\ \ Intelligent voice agents powered by LLMs Fax\ \ Send and receive faxes programmatically

Most channels can work over different transports depending on how you want to connect:

TransportWhat it isCommon uses
PSTNThe traditional phone networkCalling regular phone numbers, receiving calls from landlines and cell phones
SIPVoice over IP protocolConnecting PBX systems, desk phones, softphones, and VoIP carriers
WebRTCBrowser-based real-time communicationIn-app calling, video conferencing, browser-based contact centers

For example, a voice call could come in via PSTN (someone dialing your number), SIP (from a desk phone), or WebRTC (from your web app). SignalWire will handle all three.

Phone numbers

To make or receive calls and messages through the phone network, you’ll need SignalWire phone numbers. You can buy local numbers, toll-free numbers, or short codes directly from your Dashboard or the API.

Each number can be configured to handle incoming calls and messages differently - whether that’s forwarding to another number, running a script, connecting to an AI agent, or triggering your own application.

Learn more about Phone Numbers\ \ How to buy, configure, and manage your numbers

We also offer the option of purchasing phone numbers programmatically via our Purchase a Phone Number API Endpoint.

Resources

In SignalWire, a Resource is anything that can handle communications - an AI agent, a script, a SIP connection, or your own application. When a call or message comes in, you tell SignalWire which Resource should handle it.

Common resource types include:

  • SWML Scripts - Simple JSON/YAML instructions hosted in your Dashboard
  • AI Agents - Conversational AI that handles calls autonomously
  • Call Flows - Visual drag-and-drop call routing
  • Relay Applications - Your own server applications connected via WebSocket

Learn more about Resources\ \ Understanding the different resource types

Addresses

Every Resource has an Address. This is a unique identifier that lets you target and interact with it. Think of addresses as the phone number for any resource, but broader in scope.

Addresses can be:

  • Phone numbers - Traditional numbers like +14155551234 for PSTN calls
  • SIP addresses - For VoIP connections like sip:user@domain.com
  • Aliases - Custom names like /support-queue or /main-conference that are easy to remember

A single Resource can have multiple addresses, and you can change them anytime. For example, you might point both a phone number and a custom alias to the same AI agent.

Learn more about Addresses\ \ How addressing works in SignalWire

Subscribers

Subscribers are end users who authenticate with SignalWire to make and receive calls. If you’re building a contact center, business phone system, or video conferencing app for example, your users become Subscribers.

SignalWire manages these users for you. You create, update, and delete them through our REST APIs, and each Subscriber gets:

  • Authentication - Secure credentials and tokens for logging in
  • A callable address - They can be reached directly at /private/username
  • Multi-device support - They can answer calls from a browser, mobile app, or desk phone

This means you don’t have to build user management, authentication, or device registration yourself - SignalWire handles it.

Learn more about Subscribers\ \ User management and authentication


Next Steps

Once you’ve chosen your path, here are some resources to help you along the way:

  • Discord Community - Join 8,000+ developers. Great for questions and sharing what you’re building.
  • GitHub - Example code, SDKs, and open source tools.
  • API Reference - Detailed documentation for all our APIs.

If you get stuck or have questions, our support team is here to help at support@signalwire.com.


*Twilio and TwiML are trademarks of Twilio, Inc. SignalWire, Inc. and its products are not affiliated with or endorsed by Twilio, Inc.

The SignalWire Dashboard


International support

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Overview

SignalWire enables users to make calls and send SMS to over 230 international area codes. Please follow the steps outlined below to acquire, activate, and begin International Outbound Dialing & SMS on your SignalWire Space!

International numbers

SignalWire officially supports acquiring numbers in the following countries:

  • 🇦🇺 Australia
  • 🇨🇦 Canada
  • 🇮🇪 Ireland
  • 🇳🇿 New Zealand
  • 🇬🇧 United Kingdom
  • 🇺🇸 United States

Please submit a request form to procure an International number(s).

If you have more questions about purchasing numbers in countries other than the United States or Canada:

Contact our Sales team at sales@signalwire.comto get started!

How to request international activation

By default, SignalWire Spaces are only enabled for the US and Canada. Please submit a request form for International Services to be enabled on your Space.

A new support case will be opened and our technicians will be in contact when approved and enabled.

Please Note: Only spaces NOT in trial mode will be enabled for international services.

Enable geographic permissions

Once enabled, you will need to add the geographic permission for each country you would like to be able to dial or message. Follow the steps below to complete:

First, navigate to your signalwire Dashboard, and click on the Space dropdown menu at top left of the page:

the Space dropdown menu, with links to Members, Space Settings, and more

There select Space Settings. You’ll be taken to the Settings page.

Only administrators and owners have access to the Space Settings.

Select countries

Once in the Space Settings page, click on the Geographic Permissions tab.

Here you can check the box for the countries for which you would like to enable international Dialing/SMS.

Please Note: Messaging can only be sent from US and Canadian (CA) long code numbers. International SMS is not enabled for toll-free numbers.

Troubleshooting

If you attempt to call or message a destination in a country you have not enabled, the request fails because no route to that destination is permitted by your account. REST requests return the not_routable error code.

To resolve it, confirm International Services are active on your Space and that the destination country is checked under Geographic Permissions, following the steps above.


Media URL protection

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Media URL Protection controls access to media files generated by your SignalWire services, including call recordings, MMS attachments, and fax documents.

By default, these media URLs are publicly accessible to anyone with the direct link, but they contain randomly generated UUIDs that make them virtually impossible to discover or guess. This provides security through obscurity, but may not be sufficient for applications handling sensitive content.

When Media URL Protection is enabled, accessing these URLs requires authentication using your project’s API credentials. This uses the same authentication method as other SignalWire API endpoints. Unauthorized attempts will be denied.

For more information about API credentials and how to use them, see the API credentials documentation.

Configuring media protection

1

Select your project

Navigate to the projects page and select the project where you want to configure media protection.

2

Access project settings

Once in your project dashboard, click on the area where your project name is displayed. This should open a menu with an option called Project Configuration. Click on this option to be directed to the project settings page.

Project settings menu.

Project settings menu

3

Locate media URL protection

Scroll down to find the Media URL Protection Details section, which contains three independent toggles:

  • Protect Recording Media URLs - controls access to call recordings generated by your voice services.
  • Protect Message Media URLs - applies protection to media attachments sent through SMS and MMS messages, including images, documents, and other attached files.
  • Protect Fax Media URLs - secures fax documents and their attachments. Fax metadata and delivery confirmations remain unaffected.

Media Protection Interface.

Media URL Protection settings in your Project Dashboard

4

Enable protection

Toggle any combination of the three protection options from No to Yes based on your security requirements. Changes take effect immediately.

5

You’re protected

Now when a request is sent to a media URL, if no valid authentication is provided, the request will be denied!


Port phone numbers to SignalWire

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

After becoming more familiar with our services, you may decide to port over your existing phone numbers to SignalWire. You can port any number (wireless or business landline) to SignalWire.

If requesting E911 capabilities for numbers, please reach out to SignalWire prior to submitting the order or in the port order itself.

How to Submit a Porting Request

You can submit a port request from your SignalWire Dashboard. Click on Phone Numbers > Port Requests. Click on Initiate Port Request to complete the form in a new tab.

Below are the required fields for a successful port-in process:

  • Project ID
  • Number List
  • Current Provider Name
  • Current Provider Account Number
  • Account Type (Business / Residential)
  • Company Name
  • Authorized Name
  • Billing Phone Number
  • Phone Service Address
  • PIN

NOTE: A Letter of Authorization is also required with your port-in request. Please download, complete, and upload the appropriate Letter of Authorization (LOA) form with your request.

Hosted Messaging

Want to port in messaging services only? See our guide to Hosted Messaging.

Porting Process

Port-in of your phone number(s) is handled accordingly:

  • Your port-in order is submitted to the losing provider who must acknowledge the order if your submitted information is valid. If the information you provided is determined to be invalid for any reason, the losing carrier may reject the order.

  • If your port-in order is rejected, you will be notified. Your port-in order status will be set to rejected during this period and will not change status until you respond with the correct information.

  • When your order is accepted, the losing carrier will let us know when to expect the port to complete (FOC, or Firm Order Commitment), and the order status will be set to Scheduled.

  • SignalWire will communicate the completion (FOC) date. This is the date/time at which the number will become active in your SignalWire account.

  • When a FOC date is confirmed, the numbers are added to your account early. Please use this time to set up your number settings and webhooks. Failure to configure your number to a webhook will result in your number not working correctly.

  • SignalWire will communicate the completion (FOC) date and confirmation that the numbers have been added to your account. This is the date/time at which the number will become active in your SignalWire account.

SignalWire may need to include Customer Service Record (CSR) documentation or previous billing statements for your port-in order. We will need you to sign a Letter Of Authorization (LOA) for the port to begin processing. A signed LOA dated within the past 30 days is necessary for porting your numbers into SignalWire.

What could cause delays?

Any of the following issues could lead to delays in the porting process. Please check on your case often in the Dashboard in case we need any information corrected to finish your porting request.

  • invalid fields such as service address, billing address, and name
  • missing PINs
  • creating several orders with conflicting information
  • attempting to port inactive numbers

On average, it may take up to 7 business days for small port orders to be completed without delays. Larger port orders will take more time to complete. Toll-Free numbers, Canadian numbers, and off-net numbers can take up to two weeks.

Once you complete a form and submit it, you will receive an email with a case number. If your port request is rejected, you will receive a follow-up via email. If no response is received within a week, the port order will be canceled, and the case will be closed.


Rate limits

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

The below throughput limits apply to Voice and Messaging. These are account-level limits that count activity from all Projects within your Space. Contact Support to increase Voice and Messaging throughput.

Rate limits and backlogs for Messaging and Voice are separate. For example, exceeding the Voice rate limit does not cause outbound messages to queue.

Voice

View pricing and contact Support to increase calling throughput.

Because faxes are sent via phone calls, the below rate limits apply to faxes as well.

ValueDefault limit
Calls1 call per second (CPS)
Backlog10,000 queued calls

Messaging

View pricing and contact Support to increase messaging throughput for toll-free or short code numbers.

Number typeDefault limit
Toll-free3 messages per second (MPS)
US long code (10DLC)4 MPS <br>Actual throughput may be lower or higher depending on carrier and TCR limits
Canadian long code1 MPS
Short code10 MPS
Backlog10,000 queued messages

Larger messages are automatically split into smaller “segments” which are sent as individual messages. Each of these segments counts towards the messages per second (MPS) limit. Consult the guide to Messaging Character Limits for more information.

API

The below limits apply to all API requests across the SignalWire platform. Every HTTP request includes the user’s current limit and remaining requests in its X-Header.

HTTP verbLimit
GETDELETEEffectively unlimited
POSTPUTPATCH13800 requests per 10 seconds

Space

These limits apply to SignalWire Spaces not in Trial mode. New accounts remain in Trial mode until at least $5 USD of credit is added.

Space limitDefault
Number of ProjectsUnlimited
Phone numbers per Project1000
Verified IDs1000
Call backlog10,000
Message backlog10,000

Queue and backlog system

When an application sends calls or messages to SignalWire at a rate exceeding the applicable rate limit, SignalWire queues these messages or calls in the order received. The backlog is the limit to the number of queued (pending) calls or messages. The Voice backlog and Messaging backlog are both 10,000 by default.

When the backlog for calls is full, SignalWire will stop adding additional calls to the Voice queue. Similarly, when the backlog for messages is full, SignalWire will stop adding to the Messaging queue. The platform will process queued calls or messages at the maximum applicable rate, and add new calls or messages to the queue as long as the backlog has not been reached.

The Voice and Messaging backlogs are entirely separate.

If you would like to raise your Voice or Messaging backlog, fill out the Space Increase Request Form.

Request increased limits

Request an increase to your Space’s default limits by filling out the SignalWire Space Increase Request Form and submitting the required documentation to start the account verification process. SignalWire’s Space Compliance team reviews submissions and will notify you of the results.

Phone number limits

Requests for more than 1000 phone numbers require additional verification. Please be prepared to share detailed information about your use case.

If approved, you MUST set up auto top up on your project to the total cost of the numbers monthly before the limit will be raised. This requirement prevents the suspension of services due to insufficient balance. Pricing for phone numbers can be found on the SignalWire pricing page.


Resources

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Resources are the primary entities for communication within the Call Fabric ecosystem. They are the building blocks of the system, representing the various communication elements that can be used to interact with subscribers.

Manage Resources

With the REST API

Use the Resources endpoints to manage your Resources programmatically.

Resources endpoints\ \ Control Resources using the SignalWire REST API.

In the Dashboard

The Dashboard is your unified command center for your SignalWire Space. Access it at {your-space-name}.signalwire.com.

Create

No Resources tab?

If you don’t see the My Resources tab, your SignalWire Space is on the Legacy Dashboard. Refer to the Legacy section of this guide for instructions for your Dashboard and information about the migration.

1

To create a Resource in your SignalWire Space, click on the My Resources tab in the left-hand navigation menu.

From there, you can select the type of Resource you would like to create by clicking on the + Add button.

2

Configure the Resource

What's a Resource?

Resources are the building blocks of SignalWire applications. They include AI Agents, SWML Scripts, cXML Scripts, SIP Endpoints, and more.

Create a new Resource from the Dashboard.

Create a new Resource from the Dashboard

Click the Add button to select from the grid of available Resource types.

Select a Resource in the Signalwire Dashboard.

The Resource selection menu

Alternatively, you can create new Resources from their subpages in the sidebar menu, or using the shortcuts on the Dashboard homepage.

After selecting a Resource, enter the details for the Resource on a new page, and select Create to confirm.

Modify or delete

From the Resources page in your SignalWire Dashboard, select the desired Resource to open a detailed view. Here, by selecting the Edit button, you can alter the specifics of the Resource.

The list of Resources in a SignalWire Space.

The list of Resources in a SignalWire Space


In the Legacy Dashboard

The Resources experience is not available in the Legacy Dashboard.

To upgrade your Space to the New UI, contact Support.

Which Dashboard version am I using?

New Dashboard: You have a Resources tab in the left sidebar.

Legacy Dashboard: You have separate tabs for SIP, LaML, RELAY, etc.


Create an account

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Sign up for a SignalWire account.

1

Sign up

Initiate the process by entering an email address or selecting a supported authorization provider.

Sign up for a SignalWire account\ \ Open a new account and reserve a unique .signalwire.com Space domain.

2

Name your SignalWire Space

This is your unique .signalwire.com domain to which all services are tied. We recommend using your company name.

Keep the following in mind:

Choose wisely

Once set, your Space domain cannot be changed except by a Support request.

Multiple spaces

You can create multiple Spaces associated with your SignalWire account.

Accounts, Spaces, Projects

Each Space can contain multiple Projects.

3

Create a project

Projects are used to group resources according to your preference, such as by customer account, by geographical region, or any other classification. Name your project something that will help to distinguish it from the other projects you will make in the future, and you’re ready to go.

Now you have a SignalWire Space and your first project. Congratulations!


Next steps

Take your next steps by exploring the Dashboard, verifying a phone number for outbound Caller ID, and exiting Trial Mode by adding at least $5 in credit.

Sign up for a SignalWire account.


Subscribers

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

What are subscribers?

Subscribers are the core Resources in SignalWire’s Call Fabric platform that represent users in your communication system. More than just a simple user account, Subscribers provide a complete user management solution with built-in authentication, communication endpoints, and billing integration.

Subscribers represent users in your communication system with enterprise-grade capabilities built-in. They embody SignalWire’s approach to Programmable Unified Communications (PUC), enabling you to onboard and manage users at scale without building complex infrastructure from scratch.

What’s included

Every Subscriber comes with four core components that work together to provide a complete user management solution:

Identity & authentication

Secure user management with industry-standard credential storage for usernames and passwords. Extensible profile system for names, emails, company details, timezones, and locations.

Communication endpoints

Multi-channel connectivity with private Call Fabric address for internal communication plus public phone numbers for external calls. Full SIP, WebRTC, and PSTN support.

Flexible configuration

Customizable codec settings, encryption options, and SIP/WebRTC protocols. Configurable communication preferences and routing rules.

How it works

Subscribers are built on three core technical systems:

Resource addressing

Subscribers use Call Fabric’s unified addressing system for consistent identification across all platform Resources. Each Subscriber is uniquely identified by an address in the format /<Context>/<Name>.

Addresses make Subscribers directly callable. When a call is placed to a Subscriber’s address, Call Fabric automatically routes the call directly to that Subscriber. This enables seamless peer-to-peer communication without complex routing configuration.

Common address examples:

AddressDescription
/private/john-doeDirect dial to John’s devices
/public/support-agentPublicly accessible support line
/private/jane?channel=videoVideo call to Jane

How direct calling works:

  • When someone calls /private/john-doe, the call rings John’s registered device
  • The Subscriber can answer on any device (mobile app, web browser, desk phone)
  • Call Fabric handles device registration, presence, and simultaneous ring automatically

Subscribers default to private context for security, with optional public aliases for external access. Learn more about Resource Addresses, including contexts, naming conventions, and advanced routing options.

Authentication system

Multiple token types provide flexible security and integration options:

Subscriber Tokens\ \ Standard user authentication\ \ For End Users: Issued during login, provide access to user-specific resources. Perfect for web and mobile app authentication flows. Guest Tokens\ \ Temporary guest access\ \ For Guest Users: Allows Subscribers to create guest tokens that provide temporary access to specific Resource addresses. The guest token creates a temporary subscriber with limited permissions to access only the specified allowed_address. Invite Tokens\ \ Client-side API access\ \ For Client Applications: Creates a Subscriber Invite Token that enables client-side bearer token authentication for API calls. Refresh Tokens\ \ Session management\ \ For Seamless Experience: Handle automatic token renewal and session management without frequent re-authentication.

API management

Complete REST API with full CRUD operations for programmatic control:

Create Subscriber\ \ Create new subscribers\ \ Individual or bulk creation with profile data, credentials, and configuration settings. Retrieve Subscriber\ \ Get subscriber details\ \ Fetch individual Subscriber information, settings, and current status. Update Subscriber\ \ Modify subscriber data\ \ Update profile information, settings, credentials, and configuration options. Remove Subscriber\ \ Delete subscribers\ \ Remove Subscribers and associated data with proper cleanup and notifications.

Bulk operations handle large user populations efficiently, while webhooks provide real-time notifications for lifecycle events, authentication, and communication activities.

Integration example

Here’s how Subscribers work in practice with the Browser SDK:

Authentication
Making calls
Receiving calls
import { SignalWire, StaticCredentialProvider } from "@signalwire/js";

// Hand the Subscriber Access Token to the SDK via a credential provider.
const client = new SignalWire(
  new StaticCredentialProvider({ token: "<TOKEN>" })
);

// Watch the readiness observables to know when the client is connected.
client.ready$.subscribe((ready) => {
  console.log("SignalWire client ready:", ready);
});

Common use patterns

  • Peer-to-peer calling: Direct subscriber-to-subscriber calls using private addresses like /private/john-doe
  • Conference rooms: Multiple subscribers joining shared resources like /public/team-meeting
  • AI agent interaction: Subscribers calling AI agents at addresses like /public/support-ai
  • PSTN & SIP calling: Ability to call PSTN and SIP destinations directly from Subscriber applications

Common use cases

These technical patterns enable real-world applications across industries:

Contact centers

Agent management made simple

Secure agent onboarding, real-time presence tracking, call routing with queues, and conference bridge integration.

Business phone systems

Enterprise communication

Complete UCaaS/VoIP platforms with call routing, advanced features, multi-channel support, and user lifecycle management.

Video platforms

Personalized video experiences

User authentication, cross-platform compatibility, meeting management, and comprehensive usage analytics.

Value proposition & pricing

$3/user/month - No setup fees, hidden costs, or complex calculations. Includes complete user management, authentication, multi-channel communication, and enterprise-grade security.

Why choose subscribers?

At $3/user/month, Subscribers provide a complete user management solution with built-in infrastructure:

Complete infrastructure

User management, authentication, billing systems, and communication infrastructure included. Production-ready capabilities without custom development.

Enterprise-grade security

Built-in security controls and compliance features. Industry-standard authentication and encryption without additional implementation.

Predictable scaling

Fixed per-user pricing with no hidden costs. Infrastructure automatically scales without rearchitecting your application.

Developer-first integration

Complete REST APIs, SDKs, and comprehensive documentation. Straightforward integration with existing systems using standard protocols.

Get started

Ready to implement Subscribers? Follow these steps:

1

Create your first subscriber

Navigate to the SignalWire Dashboard and create your first Subscriber through the Resources section. This establishes your user entity and generates authentication credentials.

2

Configure user profile

Set up credentials, contact information, and Resource addresses. Configure timezone, location, and other profile details for cross-channel communication.

3

Set security context

Configure private and public contexts based on your security needs. Determine which Subscribers need public aliases and establish access controls.

4

Implementation

Use the Browser SDK or REST API to connect your application with Subscriber authentication. Implement token management and session handling.


Introduction to SWSH (SignalWire interactive shell)

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

SWSH ( S ignal W ire interactive SH ell) is a command line utility written in Python to interface with SignalWire APIs. SWSH is built to easily configure and manage SignalWire Spaces from a command-line environment.

SWSH is extremely useful in scripting contexts, and allows the user to perform repeated tasks quickly and efficiently by passing commands into SWSH.

The SWSH command prompt.

Prerequisites

Before you begin, ensure you have the following prerequisites:

  • A free SignalWire Account
  • SignalWire API Credentials: Copy your Project ID and API Token from the API tab of your SignalWire Dashboard.
  • Python 3 (or later): Python is required to install SWSH in a standalone manner.
  • Docker Desktop is required for installing SWSH as a part of the SignalWire WireStarter container. Docker is not required for installing the standalone Python package.

Installation

SWSH can be installed in two ways.

  1. Standalone: If you wish to use SWSH by itself, install its Python package.
  2. With WireStarter: SWSH can also be installed with our WireStarter Docker container, which also sets up the SignalWire SDKs and sets up a convenient development and testing environment.

Standalone Installation

MacOS or Linux
Windows

Install SWSH standalone if you’re looking for a scriptable SignalWire CLI without the additional development and testing features provided by WireStarter.

Python3 is required for the SWSH standalone installation. We also recommend using the python3 venv module, but it isn’t required.

  1. Create a new Python virtual environment

This command uses the venv module to create a new Python virtual environment named “swsh”. Before running it, navigate to the directory where you wish to store the swsh virtual environment.

python3 -m venv swsh
  1. Activate the SWSH virtual environment

This command activates the swsh virtual environment you just created. This changes your shell’s environment to use the Python and pip binaries within this environment, isolating your Python setup from the system-wide Python installation.

source swsh/bin/activate
  1. Install SWSH

This command installs the swsh package and its dependencies inside the activated virtual environment.

We’re aware of an issue with Python 3.12 causing dependency issues. Avoid these issues by specifying version 3.11 for pip.

pip3.11 install swsh
  1. Start the SWSH command prompt

Now that SWSH is installed, you can start a shell with it by running:

swsh

WireStarter (Docker Image) Installation

WireStarter is a Docker container which sets up the SignalWire SDKs and builds a development and testing environment for new developers. Install SWSH with WireStarter if you want to take advantage of WireStarter’s features.

The instructions for installing WireStarter using Docker are universal and will work on MacOS, Linux, and Windows.

  1. Start Docker Desktop

Docker must be running at the start of the process.

  1. Download and install WireStarter

WireStarter has been updated recently. If you’ve used WireStarter before, be sure to delete the previous version and reinstall the latest version of the container.

docker run --name wirestarter briankwest/wirestarter:latest
  1. Start SWSH

This command starts a terminal UI which will prompt you for relevant IDs and tokens. It will walk you through configuring the development and testing environment. It then automatically enters SWSH.

Follow the prompts on screen to enter your SignalWire Space, Project ID, and API token. Your SignalWire Space is the subdomain that prefixes signalwire.com. If your Dashboard is accessed at https://spacename.signalwire.com/dashboard, you should enter spacename when prompted for your Space. Copy the Project ID and REST API token from the API pane of your SignalWire Dashboard.

If you don’t plan to set up a WireStarter example application, you can safely ignore both ngrok prompts by leaving them blank and hitting Enter or Return.

docker exec -ti wirestarter bash

Learn more about WireStarter by visiting its repository on GitHub.


Running SWSH

Now that SWSH is installed, you can run it at any time by entering this command:

swsh

The Project ID, API Token, and API Key must be correctly configured in order to use SWSH commands.

Startup Variables

The following variables can be set to streamline startup.

They are also required for running in Non-Interactive mode. If they are not set, SWSH will ask for them at startup.

Linux / MacOS:

export PROJECT_ID=<YOUR_PROJECT_ID>
export SIGNALWIRE_SPACE=<YOUR_SIGNALWIRE_SPACE>
export REST_API_TOKEN=<YOUR_REST_API_TOKEN>

Windows:

setx PROJECT_ID=<YOUR_PROJECT_ID>
setx SIGNALWIRE_SPACE=<YOUR_SIGNALWIRE_SPACE>
setx REST_API_TOKEN=<YOUR_REST_API_TOKEN>

Commands

This section defines all SWSH commands. Commands can be run interactively or non-interactively.

Interactive Commands

Interactive commands are done within the SWSH shell itself. For example:

  1. Type swsh to enter the shell
  2. Enter the following command: phone_number list

Scriptable (Non-Interactive) Commands

This type of command can be passed into SWSH, allowing for the output from SWSH to be passed back to the main shell. This type of SWSH command is ideal for scripting. Startup variables must be set in order to enable scriptable commands.

Scriptable commands use the same syntax as Interactive commands, but condensed into a single line. For example:

swsh phone_number list

Space

Manage your SignalWire Space using the following SWSH commands:

CommandDescription
space cdChange to the specified Space
space showShow details about the current Space

Project

Manage the Project (and any subprojects) within a SignalWire Space using the following SWSH commands:

CommandDescription
project listShow a list of all projects in your SignalWire Space.
project createCreate a new Project.
project updateUpdate the Project details.
project deleteDelete a Project.

Domain Application

Domain Applications allow you to send SIP traffic to a custom domain and run specified logic. Manage Domain Applications within a SignalWire Project using the following commands:

CommandDescription
domain_application listList domain applications.
domain_application createCreate a new domain application.
domain application updateUpdate a domain application.
domain application deleteDelete a domain application.

Number Group

Number Groups are useful for configuring a set (or “group”) of numbers as a single entity. Manage Number Groups within a SignalWire Project using the following commands:

CommandDescription
number_group listList existing Number Groups in your Space.
number_group createCreate a new Number Group.
number_group updateUpdate an existing Number Group.
number_group deleteDelete a Number Group.

cXML Application

Manage cXML Applications within a SignalWire Project with these commands:

CommandDescription
laml_bin listList existing LaML bins.
laml_bin createCreate a new LaML bin.
laml_bin updateUpdate an existing LaML bin.
laml_bin deleteDelete a LaML bin.

Phone Number

Manage the SignalWire Phone Numbers within a SignalWire Project with these commands:

CommandDescription
phone_number listList the phone numbers associated with your Project.
phone_number buyBuy a new phone number.
phone_number updateUpdate an existing phone number.
phone_number deleteDelete one of our phone numbers.
phone_number lookupLook up and filter phone numbers.

SIP Endpoint

Manage SIP Endpoints within a SignalWire Project with these commands:

CommandDescription
sip_endpoint listList existing SIP Endpoints.
sip_endpoint createCreate a new SIP Endpoint.
sip_endpoint updateUpdate a SIP Endpoint.
sip_endpoint deleteDelete an existing SIP Endpoint.

SIP Profile

Manage SIP Profiles within a SignalWire Project with these commands:

CommandDescription
sip_profile listList SIP Profiles.
sip_profile updateUpdate SIP Profiles.

Make a Call

Initiate a phone call between two parties using a SignalWire Project.

The Sending Phone Number must belong to the project.

send_call --from-num <calling number> --to-num <destination number> --url <dialplan url>

Request Call Records

Retrive Call Records on a SignalWire Project

CommandDescription
get_callRetrieves a detailed list of recent calls.
get_call --id <SID of call>Retrieves call information. <br> Replace <SID of call> with the SID (SignalWire ID) of the desired call.
get_call --all-activeRetrieves call information for all active calls.

Send a Text

Transmit a text message to a destination using a SignalWire Project.

The Sending Phone Number must belong to the project.

send_text --from-num <texting number> --to-num <destination number> --text-body "<Text Body>"

Clear

Clears the screen.

clear

Exit

Exit the swsh subshell.

exit


Have a question or bug to report?

Help us improve SWSH for your use case by reporting an Issue on the SWSH GitHub Repository.


SignalWire tools

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Learn about the tools built to streamline the developer experience of building with SignalWire tools.

SWSH\ \ Manage your Spaces and Projects from the command line with the powerful and scriptable SignalWire Interactive Shell WireStarter\ \ Quickly bootstrap a versatile development and testing environment with the WireStarter Docker container, pre-loaded with demo apps for immediate use


Trial mode

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

All new SignalWire accounts begin in Trial Mode, a restricted status to prevent abuse of services. To remove Trial Mode, simply add a credit card to your account and fund it with at least $5 of credit.

Fund your account\ \ Add a payment method and fund your account

Trial mode limitations

Activity

These activities are subject to the indicated restrictions during Trial Mode.

ActivityLimitation
Make and receive callsOnly to and from purchased and verified numbers
Send and receive messagesOnly to and from purchased numbers
SIP endpointsSIP endpoints can register, but you can’t place outbound calls or receive traffic on a Domain App

Resources

These Resources are limited to the indicated maximum count during Trial Mode.

ResourceLimitation
Total phone numbers2
Simultaneous queued calls10
Simultaneous queued messages10
Verified caller IDs10

Other

  • No international calling or SMS
  • DIDs cannot be released for 30 days

Video

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

SignalWire Video is a powerful WebRTC video conferencing tool built on an MCU (Multipoint Control Unit) that handles all the muxing in the cloud. A single unified stream is sent to each participant, resulting in low latency, high quality, and the same video stream for all users.

REST APIs\ \ Use classic HTTP calls to create and maintain tokens, rooms, sessions, and recordings. SDK Video Reference\ \ Manage video rooms, sessions, recordings, and streams. Browser SDKs (v2 and v3)\ \ Make audio/video calls to phone numbers, SIP endpoints, and other browsers.

Frequently asked questions

Can I programmatically create rooms and control attendee permissions?

Yes. Please check out our API reference to see how to create a room with a prebuilt\ UI (a Programmable Video Conference) with a room without a prebuilt UI.

How long can room names be?

Room names can be up to 100 characters, with a display name of up to 200 characters. Be sure to avoid using ”/” in room names.

How can I define user permissions?

You define a user’s permissions for a room when you generate their access token. See the room_tokens API endpoint. Access tokens for Programmable Video Conferences are generated when the conference is created. The response object includes tokens for moderators and guests. Guest tokens will only have permissions to modify their own settings while moderators can effect their own settings, room settings, and settings for other participants.

Do video tokens expire?

No, by default all tokens will last indefinitely. You can alter the lifetime of your tokens using a variety of parameters, more detail here.

Are the video tokens signed or encrypted?

The video tokens are signed and are secure. Encryption cannot be implemented on the user-end without causing problems as SignalWire would then need to decrypt them.

Can users be blocked by IP address?

Not at the SignalWire/Token level. You would have to block them from your website/application.

Is the Video API encrypted?

Yes, it is encrypted through HTTPS and SRTP. WebRTC products are required to be encrypted!

Is SignalWire video HIPAA compliant?

Yes! You control access to video conferences via JWT tokens, recording is disabled by default and must be enabled manually, and recordings can be deleted from SignalWire.

Can I incorporate chat?

Yes! When building your website you will have to generate a unique token for the video room and the chat room, but both can exist on the same page.

Can I use video filters?

There is no native support for this at the moment, but you can implement it yourself using video overlays.

Why do users' cameras briefly activate when joining while muted?

SignalWire enumerates over all devices and ‘checks’ the devices where permissions allow. No video/audio data is transmitted during this test.

Does SignalWire support RTMP?

The Video API supports inbound RTMP via the play method, and outbound RTMP via the startStream method.

Where are recordings stored?

Video Room recordings can be found in your SignalWire Dashboard. Click on Video in the left sidebar, then find a list of recordings under the Recordings tab.

How can recordings be downloaded/deleted?

Video room recordings can be deleted or downloaded via your SignalWire Dashboard in the video recordings section or via the API.

What is considered a participant?

Any new member in the room - this could be a person who joined or a screen being shared.

Are Programmable Video Conferences customizable?

Yes! With Programmable Video Conferences, you can now change the background color, foreground color, primary color, success color, negative color, and switch from light mode to dark mode.

Can you record and screen share in a Programmable Video Conference?

Yes! Our Programmable Video Conferences allow for recording, screen sharing, moderator/guest access, room previews, and more, all with no code required. If you need further customizations, you can write custom code on top of the pre-built room with ease.

Do Programmable Video Conferences have automatic recording?

Yes, you can choose to start recording as soon as the room starts in the settings for Programmable Video Conferences. Even if you do not select this, moderators will still have the ability to manually start and stop recording at their own discretion. Guests will not have this ability.


Webhooks

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

Webhooks are HTTP requests sent to your server from SignalWire when an event occurs. They help receive information about events like inbound calls to your phone numbers, or messages.

In addition to getting information about events, some webhooks also allow you to tell SignalWire how an event should be handled.

During development, you can use localhost tunneling applications like ngrok to test your webhook handlers locally. See the ngrok quickstart guide to get started.

Configure webhooks for phone numbers

To handle an inbound call or message, you point your phone number at a Resource that holds your webhook URL. When an event arrives, SignalWire requests that URL and your server responds with SWML, the SignalWire Markup Language that tells SignalWire how to handle the call.

What's a Resource?

Resources are the building blocks of SignalWire applications. They include AI Agents, SWML Scripts, cXML Scripts, SIP Endpoints, and more.

1

Create a Resource for your webhook URL

In the SignalWire Dashboard, open the My Resources tab and click + Add, then choose SWML Script.

Selecting a Resource type in the SignalWire Dashboard

The Resource selection menu

Give the script a name, set Handle Calls Using to External URL, and enter your webhook URL in the Primary Script URL field. Click Create to save the Resource.

Configuring a SWML Script Resource with an external URL in the SignalWire Dashboard

A SWML Script Resource configured to fetch SWML from an external webhook URL

2

Assign the Resource to your phone number

Open the Phone Numbers tab and select the number you want to configure.

The Phone Numbers tab of a SignalWire Space showing a list of phone numbers

The purchased phone number list in the SignalWire Dashboard

Click Edit Settings. Under Inbound Call Settings (or Inbound Message Settings for messaging), choose Assign Resource, select the Resource you created, and click Save.

A phone number's settings page showing the Assign Resource option for inbound calls

Assigning a Resource to a phone number's inbound call settings

Mapping numbers\ \ A full walkthrough of connecting a phone number to your application. Phone numbers\ \ How to purchase and manage phone numbers in your SignalWire Space. Handling calls from code\ \ Learn how to handle incoming calls and messages from code.

Status callbacks to keep track of events

Status callbacks are asynchronous HTTP requests SignalWire sends to your server as a call, message, or recording moves through its lifecycle, so your application can react to each state change as it happens.

You subscribe to a status callback programmatically: when you create the call or message, provide a callback URL on the relevant SWML method, and SignalWire posts to it each time the state changes. What you set and the states you receive depend on what you’re tracking:

To trackProvide a callback URL onStates you’ll receive
Voice callscall_state_url on connectcreated, ringing, answered, ended
Messagesstatus_callback on send_sms, or status_url on replyqueued, initiated, sent, delivered, undelivered, failed, read
Recordingsstatus_url on record_callrecording, paused, finished, no_input, error

For voice calls, call_state_events defaults to ['ended'], set it explicitly to also receive created, ringing, and answered.

SignalWire only marks a message Delivered once it receives a Delivery Receipt (DLR) confirming the message reached the end carrier’s network. A status of Sent means the message left SignalWire successfully. MMS messages do not support DLRs, so they only ever show Sent.

Message status callback\ \ The full field reference and status values for outbound message status callbacks. 10DLC status callback\ \ Receive 10DLC campaign registration status updates via webhooks.

Verify webhook signature

To verify webhooks that originated from SignalWire, SignalWire signs its requests with a digital HMAC security key. You can verify that the security key matches the key documented in your Dashboard’s API Credentials with the validateRequest method.

The API Credentials page in a SignalWire Space showing the signing key

The Signing Key on the API Credentials page

This step is not optional!

For production applications, it is extremely important to verify the webhook signature to ensure the requests are coming from SignalWire and not a malicious third party.

import { validateRequest } from "@signalwire/js";

// prepare raw body for validation
app.use(express.json({
  verify: (req: any, _res, buf) => {
    req.rawBody = buf.toString();
  }
}));

app.post("/mywebhook", (req: any, res) => {
  const valid = validateRequest(
    "<SIGNING_KEY_FROM_Dashboard>",
    req.headers["x-signalwire-signature"] as string,
    "https://example.ngrok.io/mywebhook", //this should be the public-facing URL of your webhook handler
    req.rawBody
  );

  if (!valid) return res.status(401).send("Invalid signature");

  res.sendStatus(200);
});

Segment IDs

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

A Segment ID (SID) is a UUID (Universally Unique Identifier) that uniquely identifies all communication aspects on SignalWire. This includes calls, SMS, and faxes. The SID is used to log, track and identify events.

For example, a voice call will have the same Segment ID throughout the lifecycle of the call. So you can use the SID to track that call in your system or database.

How to locate your SIDs

SIDs can be found in the logs section of your SignalWire Dashboard. You will see sections related to Voice, Conferencing, Messaging, and Fax.

Open the detail view of any individual log you’d like to investigate or find the SID of.

Logs section in the SignalWire Dashboard displaying log entries with their associated SIDs.

SignalWire Dashboard logs section showing various log entries.

The SID will be displayed in the log under the “Voice Segment ID”, “Message Segment ID”, or “Fax Segment ID” section, depending on the type of communication.

In REST APIs related to calls, messages, and faxes, the SID is returned in the response as id.

Providing SIDs for support

When filing a ticket with SignalWire Support, always include the SID from the log where the incident occurred. For failing messages or calls, this SID allows for the support team to quickly isolate and investigate the issue.


What is E.164?

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

E.164 is the international telephone numbering plan used for public switched telephone networks (PSTN). It defines the format of phone numbers that ensures consistency across different telecommunications systems worldwide while ensuring that each number is unique.

E.164 numbers are typically composed of a country code, a national destination code (NDC), and a subscriber number. The maximum length of an E.164 number is 15 digits. It excludes spaces, dashes, and parentheses, and usually begins with a plus sign (+) to indicate that it is a full international number.

When dialing a number on the SignalWire platform, it is required to use the E.164 format. This ensures that the call is routed correctly to the intended destination.

E.164 Number Structure:

  • ”+” (Prefix): Indicates an international number.
  • Country Code (CC): 1-3 digits assigned to each country (e.g., +1 for the U.S., +44 for the U.K.).
  • National Destination Code (NDC): Area or region code for a specific part of the country.
  • Subscriber Number (SN): The individual’s or business’s unique phone number.

Example Table:

ScenarioE.164 FormatDescription
U.S. Mobile Number+12125551234A standard mobile number in the U.S. (country code +1)
U.K. Landline+442071838750A landline number in London, U.K. (country code +44)
India Mobile Number+919876543210A mobile number in India (country code +91)
Brazil Mobile (Sao Paulo)+5511998765432A Sao Paulo mobile number in Brazil (country code +55)
Australia Business Line+61298765432A business phone in Sydney, Australia (country code +61)
Germany Landline+492219876543A landline number in Cologne, Germany (country code +49)

Regex Pattern for E.164 Numbers:

If your application does not store numbers in E.164 format, you can use a regular expression to format them for your SignalWire application. Adhering to the official ITU E.164 recommendation the format must be a number that starts with a plus sign (+) and has a maximum length of 15 digits. Since no country code starts with a 0, the first digit after the plus sign must be a number between 1 and 9.

^\+[1-9]\d{1,14}$

To ensure the formatted number is a valid number, you can use the SignalWire Phone Number Lookup API to verify the number’s validity and retrieve additional information about it.


Introduction to WireStarter

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

WireStarter is a Docker container which sets up the SignalWire SDKs and builds a development and testing environment for new developers.

WireStarter includes SWSH (the SignalWire interactive Shell) by default. SWSH is a scriptable utility built to easily configure and manage SignalWire Spaces from a command-line environment.

Prerequisites

Before you begin, ensure you have the following prerequisites:

  • A free SignalWire Account
  • SignalWire API Credentials: Copy your Project ID and API Token from the API tab of your SignalWire Dashboard.
  • Docker Desktop

If you intend to set up an example application, you’ll also need to install ngrok and create a free ngrok account.


Installation

The instructions for installing WireStarter using Docker are universal and will work on MacOS, Linux, and Windows.

The script we run in step 3 will ask for your SignalWire Space ID, SignalWire Project ID, SignalWire Project Token, ngrok Token, and ngrok Args (optional - mostly for custom domains), so make sure you have that information handy by opening the SignalWire ‘tokens’ page and the ngrok ‘Your Authtoken’ page.

Repeat these instructions anytime you need to start WireStarter again.

1

Start Docker Desktop

Docker must be running at the start of the process.

2

Download, install, and run WireStarter

WireStarter is frequently updated. If you’ve used WireStarter before, be sure to delete the previous version (using the Docker Desktop UI or the command line) and reinstall the latest version of the container.

docker run -it -d --rm --name wirestarter briankwest/wirestarter:latest

This Docker command downloads and installs WireStarter (if it isn’t already installed). It then runs the container in the background and automatically removes it on exit.

3

Start SWSH

This command starts a terminal UI which will prompt you for relevant IDs and tokens. It will walk you through configuring the development and testing environment so you can quickly deploy and test our example applications. It then automatically enters SWSH (SignalWire Shell), the official SignalWire CLI.

Follow the prompts on screen to enter your SignalWire Space, Project ID, and API token. Your SignalWire Space is the subdomain that prefixes .signalwire.com. In other words, if your Dashboard is accessed at https://spacename.signalwire.com/dashboard, you should enter spacename when prompted for your Space. Copy the Project ID and REST API token from the API pane of your SignalWire Dashboard.

docker exec -ti wirestarter bash

The WireStarter interface. The user is prompted to enter their SignalWire Space domain.

When prompted, select your preferred editor: nano, vim, or emacs. We recommend nano for beginners.

Copy the SignalWire Space ID, Project ID, and Project token from the API pane of your SignalWire Dashboard. Copy the ngrok Token from the Your Authtoken ngrok page. ngrok Args are optional and only needed if you want to use a custom ngrok domain name. If not, leave the text field blank and proceed.

If you don’t plan to set up a WireStarter example application, you can safely ignore both ngrok prompts by leaving them blank and hitting Enter or Return.

Learn more about WireStarter by visiting its repository on GitHub.

4

Optional: Buy and set up a phone number

After entering the SignalWire and ngrok information in the previous step, you will enter the SWSH CLI. If you already have a number set up in your Space that you want to use, you can skip this step.

Phone numbers purchased in the Dashboard UI must be named before being assigned to these demos. Any name will work, just edit and save the populated number as its own name. Numbers purchased in SWSH are not affected by this limitation.

The SWSH terminal.

Purchase and configure a phone number using the phone_number command.

Enter help or help -v to view more options for SWSH.

You can also purchase a phone number in the Web UI in your SignalWire Space.

Purchased phone numbers

5

Set Startup Variables

Set the following variables from the same bash prompt in order to streamline startup. These variables are required for running SWSH in Non-Interactive mode. If they are not set, SWSH will ask for them at startup.

Linux / MacOS

export PROJECT_ID=<YOUR_PROJECT_ID>
export SIGNALWIRE_SPACE=<YOUR_SIGNALWIRE_SPACE>
export REST_API_TOKEN=<YOUR_REST_API_TOKEN>

Windows

setx PROJECT_ID=<YOUR_PROJECT_ID>
setx SIGNALWIRE_SPACE=<YOUR_SIGNALWIRE_SPACE>
setx REST_API_TOKEN=<YOUR_REST_API_TOKEN>

Learn more by reading our Introduction to SWSH.

6

Build an Example Application

Follow these instructions to build and test an example application!

Run exit to Exit SWSH

Once WireStarter is successfully configured, and you’ve purchased any numbers with SWSH, run exit once to exit SWSH.

Run setup.sh

From the same bash prompt, run setup.sh and select a demo. Follow the instructions to assign a phone number and finish setup.

If you encounter any problems using WireStarter, please let us know by reporting an issue on its GitHub repo.

You can also get live support in the Community Discord, or by emailing devex@signalwire.com.

The setup script.


Help

Have a question or bug to report?

Help us improve WireStarter for your use case by reporting an Issue on the GitHub Repository.


API credentials

For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.

The API credentials found on this page are your key to accessing SignalWire’s APIs and SDKs.

The API Credentials page on the SignalWire Dashboard.

The API Credentials page on the SignalWire Dashboard.

Most SignalWire API endpoints require authentication using HTTP Basic Auth. HTTP Basic Authentication requires you to send an Authorization header with your Project ID and API Token.

Each project has its own Project ID and API Authentication Tokens you will need to use when making a request to the API. Some methods will also require you to pass your Space URL.

  • Project ID: Use this UUID to specify your Project to the API.
  • Space URL: Use this URL to access SignalWire APIs. For example: https://{Your_Space_Name}.signalwire.com/api/calling/calls
  • API Tokens: Authentication tokens to access the API. You can have multiple tokens for each project.

API tokens are protected and used server-side to access the API. The tokens we refer to when using Video, Chat, or Fabric products in the browser are not the same tokens we generate and store here. Instead, you use your API token to call an endpoint that generates a client-side token:

  • Video Room Tokens: Generated via the Create Room Token endpoint. See our Simple Video Demo.
  • Chat Tokens: Generated for chat applications. See our Simple Chat Demo.
  • Subscriber Access Tokens (SAT): Generated via the Create Subscriber Token endpoint for Fabric applications.

For more details on authentication methods, see the REST API Authorization guide.

Generate a new API token

To generate a new API token, click the blue ”+ New” button. A “New API Token” form will open. Give your token a descriptive name to help differentiate it in logs and for debugging.

You can edit the token name and allowed scopes later by clicking the button and selecting “Edit.” You may also delete a token from the same dropdown menu or the Edit page.

Token scopes

API tokens can be configured with specific scopes that limit which APIs the token can access. When creating or editing an API token in the Dashboard, you can select which scopes to enable under the “Permissions” section.

For security best practices, only enable the scopes your application actually needs. This limits the potential impact if a token is compromised.

If your API request returns a 401 Unauthorized error, verify that your API token has the required scope enabled for that endpoint in the Dashboard.

Use an API token

All API requests must be made with proper authentication over HTTPS. Calls made over plain HTTP or without auth will fail.

Find the correct credentials structure for each specific call in the SDK Reference, REST API Reference, or in a specific Guide you may wish to follow.

For a general example, we can look at how to list video rooms with the REST API:

cURL
Node.js

This example uses curl with the -u flag to make a request with Basic Auth.

curl https://<space-name>.signalwire.com/api/video/rooms \
  -u '<project ID>:<API token>'

Or with the Realtime SDK, you may use these credentials to create a Video Client.

import { Video } from "@signalwire/realtime-api";

const video = new Video.Client({
  project: "<project ID>",
  token: "<API token>",
});

Note that you are not required to pass the Space URL when working with the SDK.

SignalWire Developer Documentation