***

title: Call Flow Customization
description: Control call flow with verb insertion points for pre-answer, post-answer, and post-AI actions.
slug: /guides/call-flow
max-toc-depth: 3
---------------------

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

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

### Understanding Call Flow

By default, [`AgentBase`][ref-agentbase] generates a simple call flow:

```text
answer -> ai
```

The SDK provides three insertion points to customize this flow:

<Frame caption="Call flow insertion points">
  <img class="diagram" src="https://files.buildwithfern.com/signalwire.docs.buildwithfern.com/docs/a85f1c4a852ea56438815c08d89b487b4bb8505be2928e147047d9b128e352cb/assets/images/sdks/diagrams/03_07_call-flow_diagram1.webp" alt="Call flow insertion points for pre-answer, post-answer, and post-AI verbs." />
</Frame>

### Verb Insertion Methods

| Method                   | Purpose                 | Common Uses                  |
| ------------------------ | ----------------------- | ---------------------------- |
| `add_pre_answer_verb()`  | Before answering        | Ringback, screening, routing |
| `add_post_answer_verb()` | After answer, before AI | Announcements, disclaimers   |
| `add_post_ai_verb()`     | After AI ends           | Cleanup, transfers, surveys  |

The verb insertion methods are available in all languages:

| Language   | Pre-Answer                          | Post-Answer                          | Post-AI                          |
| ---------- | ----------------------------------- | ------------------------------------ | -------------------------------- |
| Python     | `add_pre_answer_verb(verb, params)` | `add_post_answer_verb(verb, params)` | `add_post_ai_verb(verb, params)` |
| TypeScript | `addPreAnswerVerb(verb, params)`    | `addPostAnswerVerb(verb, params)`    | `addPostAiVerb(verb, params)`    |

### Pre-Answer Verbs

Pre-answer verbs run while the call is still ringing. Use them for:

* **Ringback tones**: Play audio before answering
* **Call screening**: Check caller ID or time
* **Conditional routing**: Route based on variables

<Tabs>
  <Tab title="Python">
    ```python
    #!/usr/bin/env python3
    from signalwire import AgentBase


    class RingbackAgent(AgentBase):
        """Agent that plays ringback tone before answering."""

        def __init__(self):
            super().__init__(name="ringback", port=3000)

            # Play US ringback tone before answering
            # IMPORTANT: auto_answer=False prevents play from answering the call
            self.add_pre_answer_verb("play", {
                "urls": ["ring:us"],
                "auto_answer": False
            })

            # Configure AI
            self.add_language("English", "en-US", "rime.spore")
            self.prompt_add_section("Role", "You are a helpful assistant.")


    if __name__ == "__main__":
        agent = RingbackAgent()
        agent.run()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { AgentBase } from 'signalwire-agents';
    const agent = new AgentBase({ name: 'ringback', port: 3000 });
    agent.addPreAnswerVerb('play', { urls: ['ring:us'], auto_answer: false });
    agent.addLanguage({ name: 'English', code: 'en-US', voice: 'rime.spore' });
    agent.promptAddSection('Role', { body: 'You are a helpful assistant.' });
    agent.run();
    ```
  </Tab>
</Tabs>

**Generated SWML:**

```json
{
  "sections": {
    "main": [
      {"play": {"urls": ["ring:us"], "auto_answer": false}},
      {"answer": {}},
      {"ai": {...}}
    ]
  }
}
```

#### Pre-Answer Safe Verbs

Only certain verbs can run before the call is answered:

| Verb       | Pre-Answer Safe | Notes                         |
| ---------- | --------------- | ----------------------------- |
| `play`     | Yes\*           | Requires `auto_answer: false` |
| `connect`  | Yes\*           | Requires `auto_answer: false` |
| `sleep`    | Yes             | Wait for duration             |
| `set`      | Yes             | Set variables                 |
| `request`  | Yes             | HTTP request                  |
| `switch`   | Yes             | Variable-based branching      |
| `cond`     | Yes             | Conditional branching         |
| `if`       | Yes             | If/then/else                  |
| `eval`     | Yes             | Evaluate expressions          |
| `goto`     | Yes             | Jump to label                 |
| `label`    | Yes             | Define jump target            |
| `hangup`   | Yes             | Reject call                   |
| `transfer` | Yes             | Route elsewhere               |

\*These verbs auto-answer by default. Set `auto_answer: false` for pre-answer use.

#### Available Ringback Tones

| Tone      | Description            |
| --------- | ---------------------- |
| `ring:us` | US ringback tone       |
| `ring:uk` | UK ringback tone       |
| `ring:it` | Italian ringback tone  |
| `ring:at` | Austrian ringback tone |

### Post-Answer Verbs

Post-answer verbs run after the call is connected but before the AI speaks:

<Tabs>
  <Tab title="Python">
    ```python
    #!/usr/bin/env python3
    from signalwire import AgentBase


    class WelcomeAgent(AgentBase):
        """Agent that plays welcome message before AI."""

        def __init__(self):
            super().__init__(name="welcome", port=3000)

            # Play welcome announcement
            self.add_post_answer_verb("play", {
                "url": "say:Thank you for calling Acme Corporation. "
                       "Your call may be recorded for quality assurance."
            })

            # Brief pause before AI speaks
            self.add_post_answer_verb("sleep", {"time": 500})

            # Configure AI
            self.add_language("English", "en-US", "rime.spore")
            self.prompt_add_section(
                "Role",
                "You are a customer service representative. "
                "The caller has just heard the welcome message."
            )


    if __name__ == "__main__":
        agent = WelcomeAgent()
        agent.run()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { AgentBase } from 'signalwire-agents';
    const agent = new AgentBase({ name: 'welcome', port: 3000 });
    agent.addPostAnswerVerb('play', {
      url: 'say:Thank you for calling Acme Corporation. Your call may be recorded for quality assurance.',
    });
    agent.addPostAnswerVerb('sleep', { time: 500 });
    agent.addLanguage({ name: 'English', code: 'en-US', voice: 'rime.spore' });
    agent.promptAddSection('Role', {
      body: 'You are a customer service representative. The caller has just heard the welcome message.',
    });
    agent.run();
    ```
  </Tab>
</Tabs>

**Generated SWML:**

```json
{
  "sections": {
    "main": [
      {"answer": {}},
      {"play": {"url": "say:Thank you for calling..."}},
      {"sleep": {"time": 500}},
      {"ai": {...}}
    ]
  }
}
```

#### Common Post-Answer Uses

| Use Case         | Example                                       |
| ---------------- | --------------------------------------------- |
| Welcome message  | `{"url": "say:Thank you for calling..."}`     |
| Legal disclaimer | `{"url": "say:This call may be recorded..."}` |
| Hold music       | `{"url": "https://example.com/hold.mp3"}`     |
| Pause            | `{"time": 500}` (milliseconds)                |
| Recording        | Use `record_call=True` in constructor         |

### Post-AI Verbs

Post-AI verbs run after the AI conversation ends:

```python
#!/usr/bin/env python3
from signalwire import AgentBase


class SurveyAgent(AgentBase):
    """Agent that logs call outcome after conversation."""

    def __init__(self):
        super().__init__(name="survey", port=3000)

        # Configure AI
        self.add_language("English", "en-US", "rime.spore")
        self.prompt_add_section("Role", "You are a support agent.")

        # After AI ends, log the call and hang up
        self.add_post_ai_verb("request", {
            "url": "https://api.example.com/call-complete",
            "method": "POST"
        })
        self.add_post_ai_verb("hangup", {})


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

#### Common Post-AI Uses

| Use Case          | Verb          | Example                        |
| ----------------- | ------------- | ------------------------------ |
| Clean disconnect  | `hangup`      | `{}`                           |
| Transfer to human | `transfer`    | `{"dest": "tel:+15551234567"}` |
| Post-call survey  | `prompt`      | DTMF collection                |
| Log outcome       | `request`     | HTTP POST to API               |
| Connect to queue  | `enter_queue` | `{"name": "support"}`          |

### Complete Example

Here's an agent with all three insertion points:

<Tabs>
  <Tab title="Python">
    ```python
    #!/usr/bin/env python3
    from signalwire import AgentBase


    class CallFlowAgent(AgentBase):
        """Agent demonstrating complete call flow customization."""

        def __init__(self):
            super().__init__(name="call-flow", port=3000)

            # PRE-ANSWER: Ringback tone
            self.add_pre_answer_verb("play", {
                "urls": ["ring:us"],
                "auto_answer": False
            })

            # POST-ANSWER: Welcome and disclaimer
            self.add_post_answer_verb("play", {
                "url": "say:Welcome to Acme Corporation."
            })
            self.add_post_answer_verb("play", {
                "url": "say:This call may be recorded for quality assurance."
            })
            self.add_post_answer_verb("sleep", {"time": 500})

            # Configure AI
            self.add_language("English", "en-US", "rime.spore")
            self.prompt_add_section(
                "Role",
                "You are a friendly customer service representative. "
                "The caller has just heard the welcome message."
            )
            self.set_params({
                "end_of_speech_timeout": 1000,
                "attention_timeout": 10000
            })

            # POST-AI: Clean disconnect
            self.add_post_ai_verb("hangup", {})


    if __name__ == "__main__":
        agent = CallFlowAgent()
        agent.run()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { AgentBase } from 'signalwire-agents';
    const agent = new AgentBase({ name: 'call-flow', port: 3000 });
    agent.addPreAnswerVerb('play', { urls: ['ring:us'], auto_answer: false });
    agent.addPostAnswerVerb('play', { url: 'say:Welcome to Acme Corporation.' });
    agent.addPostAnswerVerb('play', { url: 'say:This call may be recorded for quality assurance.' });
    agent.addPostAnswerVerb('sleep', { time: 500 });
    agent.addLanguage({ name: 'English', code: 'en-US', voice: 'rime.spore' });
    agent.promptAddSection('Role', {
      body: 'You are a friendly customer service representative. The caller has just heard the welcome message.',
    });
    agent.setParams({ end_of_speech_timeout: 1000, attention_timeout: 10000 });
    agent.addPostAiVerb('hangup', {});
    agent.run();
    ```
  </Tab>
</Tabs>

**Generated SWML:**

```json
{
  "sections": {
    "main": [
      {"play": {"urls": ["ring:us"], "auto_answer": false}},
      {"answer": {}},
      {"play": {"url": "say:Welcome to Acme Corporation."}},
      {"play": {"url": "say:This call may be recorded..."}},
      {"sleep": {"time": 500}},
      {"ai": {...}},
      {"hangup": {}}
    ]
  }
}
```

### Controlling Answer Behavior

#### Disable Auto-Answer

Set `auto_answer=False` to prevent automatic answering:

```python
class ManualAnswerAgent(AgentBase):
    def __init__(self):
        # Disable auto-answer
        super().__init__(name="manual", port=3000, auto_answer=False)

        # Pre-answer: Play ringback
        self.add_pre_answer_verb("play", {
            "urls": ["ring:us"],
            "auto_answer": False
        })
```

#### Customize Answer Verb

Use `add_answer_verb()` to configure the answer verb:

```python
# Set max call duration to 1 hour
agent.add_answer_verb({"max_duration": 3600})
```

### Dynamic Call Flow

Modify call flow based on caller information using `on_swml_request()`:

```python
class DynamicFlowAgent(AgentBase):
    def __init__(self):
        super().__init__(name="dynamic", port=3000)
        self.add_language("English", "en-US", "rime.spore")
        self.prompt_add_section("Role", "You are a receptionist.")

        # VIP numbers get special treatment
        self.vip_numbers = ["+15551234567", "+15559876543"]

    def on_swml_request(self, request_data=None, callback_path=None, request=None):
        call_data = (request_data or {}).get("call", {})
        caller = call_data.get("from", "")

        if caller in self.vip_numbers:
            # VIP: No ringback, immediate welcome
            self.clear_pre_answer_verbs()
            self.add_post_answer_verb("play", {
                "url": "say:Welcome back, valued customer!"
            })
        else:
            # Regular caller: Ringback tone
            self.add_pre_answer_verb("play", {
                "urls": ["ring:us"],
                "auto_answer": False
            })
```

### Clear Methods

Remove verbs from insertion points:

```python
agent.clear_pre_answer_verbs()   # Remove all pre-answer verbs
agent.clear_post_answer_verbs()  # Remove all post-answer verbs
agent.clear_post_ai_verbs()      # Remove all post-AI verbs
```

### Method Chaining

All verb insertion methods return `self` for chaining:

```python
agent = AgentBase(name="chained", port=3000)
agent.add_pre_answer_verb("play", {"urls": ["ring:us"], "auto_answer": False}) \
     .add_post_answer_verb("play", {"url": "say:Welcome"}) \
     .add_post_answer_verb("sleep", {"time": 500}) \
     .add_post_ai_verb("hangup", {})
```