***

title: SWAIG (SignalWire AI Gateway)
description: SWAIG connects the AI conversation to your backend logic, letting the AI call functions you define via webhooks.
slug: /guides/swaig
max-toc-depth: 3
---------------------

For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

[lifecycle]: /docs/server-sdks/guides/lifecycle

[ref-agentbase]: /docs/server-sdks/reference/python/agents/agent-base

[ref-datamap]: /docs/server-sdks/reference/python/agents/data-map

[ref-functionresult]: /docs/server-sdks/reference/python/agents/function-result

### What is SWAIG?

SWAIG (SignalWire AI Gateway) connects the AI conversation to your backend logic. When the AI decides it needs to perform an action (like looking up an order or checking a balance), it calls a SWAIG function that you've defined.

<Frame caption="SWAIG Function Flow">
  <img class="diagram" src="https://files.buildwithfern.com/signalwire.docs.buildwithfern.com/docs/082ea036d51812af39d07992e9adca9fe8270f1e318b292adc5f47b031a862b4/assets/images/sdks/diagrams/02_03_swaig_diagram1.webp" alt="SWAIG function call flow." />
</Frame>

### SWAIG in SWML

When your agent generates SWML, it includes SWAIG function definitions in the `ai` verb:

```json
{
  "version": "1.0.0",
  "sections": {
    "main": [
      {
        "ai": {
          "SWAIG": {
            "defaults": {
              "web_hook_url": "https://your-agent.com/swaig"
            },
            "functions": [
              {
                "function": "get_balance",
                "description": "Get the customer's current account balance",
                "parameters": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string",
                      "description": "The customer's account ID"
                    }
                  },
                  "required": ["account_id"]
                }
              }
            ]
          }
        }
      }
    ]
  }
}
```

### Defining SWAIG Functions

There are three ways to define SWAIG functions in your agent:

#### Method 1: define\_tool()

The most explicit way to register a function:

<Tabs>
  <Tab title="Python">
    ```python
    from signalwire import AgentBase, FunctionResult

    class MyAgent(AgentBase):
        def __init__(self):
            super().__init__(name="my-agent")

            self.define_tool(
                name="get_balance",
                description="Get account balance for a customer",
                parameters={
                    "type": "object",
                    "properties": {
                        "account_id": {
                            "type": "string",
                            "description": "The account ID to look up"
                        }
                    },
                    "required": ["account_id"]
                },
                handler=self.get_balance
            )

        def get_balance(self, args, raw_data):
            account_id = args.get("account_id")
            return FunctionResult(f"Account {account_id} has a balance of $150.00")
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    agent.defineTool({
      name: 'get_balance',
      description: 'Get account balance for a customer',
      parameters: {
        account_id: { type: 'string', description: 'The account ID to look up' },
      },
      handler: (args) => {
        return new FunctionResult(`Account ${args.account_id} has a balance of $150.00`);
      },
    });
    ```
  </Tab>
</Tabs>

#### Method 2: @[AgentBase][ref-agentbase].tool Decorator

A cleaner approach using decorators:

```python
from signalwire import AgentBase, FunctionResult

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(name="my-agent")

    @AgentBase.tool(
        name="get_balance",
        description="Get account balance for a customer",
        parameters={
            "type": "object",
            "properties": {
                "account_id": {
                    "type": "string",
                    "description": "The account ID to look up"
                }
            },
            "required": ["account_id"]
        }
    )
    def get_balance(self, args, raw_data):
        account_id = args.get("account_id")
        return FunctionResult(f"Account {account_id} has a balance of $150.00")
```

#### Method 3: [DataMap][ref-datamap] (Serverless)

For direct API integration without code:

```python
from signalwire import AgentBase

class MyAgent(AgentBase):
    def __init__(self):
        super().__init__(name="my-agent")

        self.data_map.add_tool(
            name="get_balance",
            description="Get account balance",
            parameters={
                "account_id": {
                    "type": "string",
                    "description": "The account ID"
                }
            },
            data_map={
                "webhooks": [
                    {
                        "url": "https://api.example.com/accounts/${enc:args.account_id}/balance",
                        "method": "GET",
                        "headers": {
                            "Authorization": "Bearer ${env.API_KEY}"
                        },
                        "output": {
                            "response": "Account balance is $${balance}",
                            "action": [{"set_global_data": {"balance": "${balance}"}}]
                        }
                    }
                ]
            }
        )
```

### Function Handler Signature

Every SWAIG function handler receives two arguments -- the parsed arguments and the full request payload:

| Language   | Handler Signature                                                                     |
| ---------- | ------------------------------------------------------------------------------------- |
| Python     | `def handler(self, args: dict, raw_data: dict)`                                       |
| TypeScript | `(args: Record<string, unknown>, rawData: Record<string, unknown>) => FunctionResult` |

#### The raw\_data Payload

The `raw_data` contains rich context about the call:

```python
def my_function(self, args, raw_data):
    # Call metadata
    # Call information (nested under 'call' key)
    call_data = raw_data.get("call", {})
    call_id = call_data.get("call_id") or raw_data.get("call_id")  # Fallback for compatibility
    call_sid = raw_data.get("call_sid")

    # Caller information (from nested call object)
    from_number = call_data.get("from") or call_data.get("from_number")
    to_number = call_data.get("to") or call_data.get("to_number")

    # Global data (shared state)
    global_data = raw_data.get("global_data", {})
    customer_name = global_data.get("customer_name")

    # Conversation context
    meta_data = raw_data.get("meta_data", {})

    return FunctionResult("Processed")
```

### FunctionResult

Always return a [`FunctionResult`][ref-functionresult] from your handlers. The class name varies by language:

| Language   | FunctionResult Constructor |
| ---------- | -------------------------- |
| Python     | `FunctionResult("text")`   |
| TypeScript | `FunctionResult("text")`   |

<Tabs>
  <Tab title="Python">
    ```python
    from signalwire import FunctionResult

    def simple_response(self, args, raw_data):
        # Simple text response - AI will speak this
        return FunctionResult("Your order has been placed successfully.")

    def response_with_actions(self, args, raw_data):
        result = FunctionResult("Transferring you now.")
        result.connect("+15551234567", final=True, from_addr="+15559876543")
        return result

    def response_with_data(self, args, raw_data):
        result = FunctionResult("I've saved your preferences.")
        result.update_global_data({"user_preference": "email", "confirmed": True})
        return result
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    // Simple response
    return new FunctionResult('Your order has been placed successfully.');

    // With transfer
    return new FunctionResult('Transferring you now.')
      .connect('+15551234567', { final: true });

    // With data
    return new FunctionResult("I've saved your preferences.")
      .updateGlobalData({ user_preference: 'email', confirmed: true });
    ```
  </Tab>
</Tabs>

### Common Actions

| Action             | Purpose                      | Example                                      |
| ------------------ | ---------------------------- | -------------------------------------------- |
| `set_global_data`  | Store data for later use     | `{"key": "value"}`                           |
| `transfer`         | End AI, prepare for transfer | `True`                                       |
| `swml`             | Execute SWML after AI ends   | `{"version": "1.0.0", ...}`                  |
| `stop`             | End the AI conversation      | `True`                                       |
| `toggle_functions` | Enable/disable functions     | `[{"active": false, "function": "fn_name"}]` |
| `say`              | Speak text immediately       | `"Please hold..."`                           |
| `play_file`        | Play audio file              | `"https://example.com/hold_music.mp3"`       |

### SWAIG Request Flow

<Frame caption="SWAIG Request Processing">
  <img class="diagram" src="https://files.buildwithfern.com/signalwire.docs.buildwithfern.com/docs/f2203e3cce21ecdc85ce6c42c29a87f4752c5f382e6a77aa4c721e2deafaf04e/assets/images/sdks/diagrams/02_03_swaig_diagram2.webp" alt="SWAIG request processing flow." />
</Frame>

### SWAIG Request Format

SignalWire sends a POST request with this structure:

```json
{
  "action": "swaig_action",
  "function": "get_balance",
  "argument": {
    "parsed": [
      {
        "account_id": "12345"
      }
    ],
    "raw": "{\"account_id\": \"12345\"}"
  },
  "call": {
    "call_id": "uuid-here",
    "from": "+15551234567",
    "from_number": "+15551234567",
    "to": "+15559876543",
    "to_number": "+15559876543",
    "direction": "inbound"
  },
  "call_id": "uuid-here",
  "call_sid": "call-sid-here",
  "global_data": {
    "customer_name": "John Doe"
  },
  "meta_data": {},
  "ai_session_id": "session-uuid"
}
```

<Note>
  Call information (caller/callee numbers, call\_id, direction) is **nested under the `call` key**. Always use defensive access: `call_data = raw_data.get("call", {})`. Some fields may also appear at the top level for backwards compatibility.
</Note>

### SWAIG Response Format

Your agent responds with:

```json
{
  "response": "The account balance is $150.00",
  "action": [
    {
      "set_global_data": {
        "last_balance_check": "2024-01-15T10:30:00Z"
      }
    }
  ]
}
```

Or for a transfer:

```json
{
  "response": "Transferring you to a specialist now.",
  "action": [
    {"transfer": true},
    {
      "swml": {
        "version": "1.0.0",
        "sections": {
          "main": [
            {"connect": {"to": "+15551234567", "from": "+15559876543"}}
          ]
        }
      }
    }
  ]
}
```

### Function Parameters (JSON Schema)

SWAIG functions use JSON Schema for parameter definitions:

```python
self.define_tool(
    name="search_orders",
    description="Search customer orders",
    parameters={
        "type": "object",
        "properties": {
            "customer_id": {
                "type": "string",
                "description": "Customer ID to search for"
            },
            "status": {
                "type": "string",
                "enum": ["pending", "shipped", "delivered", "cancelled"],
                "description": "Filter by order status"
            },
            "limit": {
                "type": "integer",
                "description": "Maximum number of results",
                "default": 10
            },
            "include_details": {
                "type": "boolean",
                "description": "Include full order details",
                "default": False
            }
        },
        "required": ["customer_id"]
    },
    handler=self.search_orders
)
```

### Webhook Security

SWAIG endpoints support multiple security layers:

1. **Basic Authentication**: HTTP Basic Auth on all requests
2. **Function Tokens**: Per-function security tokens
3. **HTTPS**: TLS encryption in transit

```python
## Function-specific token security
self.define_tool(
    name="sensitive_action",
    description="Perform a sensitive action",
    parameters={...},
    handler=self.sensitive_action,
    secure=True  # Enables per-function token validation
)
```

### Testing SWAIG Functions

Use `swaig-test` to test functions locally:

```bash
## List all registered functions
swaig-test my_agent.py --list-tools

## Execute a function with arguments
swaig-test my_agent.py --exec get_balance --account_id 12345

## View the SWAIG configuration in SWML
swaig-test my_agent.py --dump-swml | grep -A 50 '"SWAIG"'
```

### Best Practices

1. **Keep functions focused**: One function, one purpose
2. **Write clear descriptions**: Help the AI understand when to use each function
3. **Validate inputs**: Check for required arguments
4. **Handle errors gracefully**: Return helpful error messages
5. **Use global\_data**: Share state between function calls
6. **Log for debugging**: Track function calls and responses

```python
def get_balance(self, args, raw_data):
    account_id = args.get("account_id")

    if not account_id:
        return FunctionResult(
            "I need an account ID to look up the balance. "
            "Could you provide your account number?"
        )

    try:
        balance = self.lookup_balance(account_id)
        return FunctionResult(f"Your current balance is ${balance:.2f}")
    except AccountNotFoundError:
        return FunctionResult(
            "I couldn't find an account with that ID. "
            "Could you verify the account number?"
        )
```

### Next Steps

Now that you understand how SWAIG connects AI to your code, let's trace the complete [lifecycle][lifecycle] of a request through the system.