> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goguava.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Warm Transfers

> Brief a human representative before connecting the caller

A warm transfer briefs a human representative before the caller is connected. In this guide, you'll build a warm transfer where:

1. An **inbound agent** answers the call and collects the caller's reason for calling.
2. While the inbound agent keeps the caller on the line, an **outbound agent** calls a customer service representative and briefs them.
3. Once the representative is ready, both calls are transferred into a shared conference.

Both agents run on the same [Runner](/sdk-reference/runner), using a phone number from your Guava account.

## Inbound agent collects the reason and stalls

The inbound agent collects `reason_for_calling`. Once that task is complete, the agent is given a new task: keep the caller on the line while a representative is found.

```python main.py theme={null}
runner = guava.Runner()

agent = guava.Agent(
    name="Nova",
    organization="Acme Corp",
)

@agent.on_call_start
def on_call_start(call: guava.Call):
    call.set_task(task_id="collect_reason", checklist=[guava.Field(key="reason_for_calling")])

@agent.on_task_complete("collect_reason")
def on_collect_reason(call: guava.Call):
    call.set_task(
        "stall_customer",
        """Inform the caller that you're checking if a customer service
                    representative is ready to help them. Stay with the caller on the
                    line until the expert notifies you that the representative is ready.""",
    )
    ...
```

## Calling the representative

In the same handler, generate a random conference ID and use `runner.call_phone()` to start an outbound call to the representative.

The conference ID, the inbound call's ID, and the reason for calling are passed to the outbound agent as [variables](/sdk-reference/get-set-variable).

```python main.py theme={null}
conference_id = secrets.token_hex(10)

runner.call_phone(
    outbound_agent,
    agent_number,
    transfer_destination,
    variables={
        "conference_id": conference_id,
        "original_call_id": call.id,
        "call_reason": call.get_field("reason_for_calling"),
    },
)
```

## Outbound agent briefs the representative

The outbound agent reads `call_reason` with `call.get_variable()`, explains the caller's issue to the representative, and confirms that they're ready to take the call.

```python main.py theme={null}
outbound_agent = guava.Agent(
    name="Nova",
    organization="Acme Corp",
    purpose="You are an outbound calling agent. You talk to customer service representatives on behalf of callers and brief them before a warm transfer.",
)

@outbound_agent.on_call_start
def on_outbound_call_start(call: guava.Call):
    call.set_task(
        "confirm_ready",
        f"""A caller is calling about {call.get_variable("call_reason")}. You are not talking to the caller - you are talking to a customer service representative.""",
        checklist=[
            "Inform the customer service agent of the caller's reason for calling.",
            "Determine if the service agent is ready for a transfer.",
        ],
    )
```

## Both calls join the conference

Once the representative is ready, the outbound agent uses `guava.RemoteCall` with `original_call_id` to control the waiting inbound call. It then [transfers](/sdk-reference/transfer) both calls to `conference:<conference_id>`, which joins them together. Each agent says a short message before it transfers.

```python main.py theme={null}
@outbound_agent.on_task_complete("confirm_ready")
def on_confirm_ready(call: guava.Call):
    conference_id = call.get_variable("conference_id")
    other_call = guava.RemoteCall(call.get_variable("original_call_id"))

    destination = f"conference:{conference_id}"
    other_call.transfer(destination, "The customer service representative is ready.")
    call.transfer(
        destination, "Transfer to the caller. Tell the rep that you are 'Patching in the caller.'"
    )
```

## Run the example

This example ships with the Python SDK. Pass the representative's phone number with `--transfer-destination`:

```bash theme={null}
python -m guava.examples.warm_transfer --transfer-destination +15555550123
```

Use `--agent-number` if you want to use a specific agent number from your account.

Call your agent's number. After you give a reason for calling, the representative's phone will ring. Once they've been briefed, you'll both be connected in a conference.

## Full example

```python main.py theme={null}
import argparse
import guava
import secrets
import logging

from guava import logging_utils
from guava.examples import get_agent_number


logger = logging.getLogger("warm_transfer_example")


runner = guava.Runner()

agent = guava.Agent(
    name="Nova",
    organization="Acme Corp",
)


@agent.on_call_start
def on_call_start(call: guava.Call):
    call.set_task(task_id="collect_reason", checklist=[guava.Field(key="reason_for_calling")])


@agent.on_task_complete("collect_reason")
def on_collect_reason(call: guava.Call):
    call.set_task(
        "stall_customer",
        """Inform the caller that you're checking if a customer service
                    representative is ready to help them. Stay with the caller on the
                    line until the expert notifies you that the representative is ready.""",
    )

    # Generate a random conference ID that will be eventually used to join the two calls.
    conference_id = secrets.token_hex(10)

    # Add this new outbound call onto our existing runner. Pass this call's ID so the
    # outbound call can transfer it later.
    runner.call_phone(
        outbound_agent,
        agent_number,
        transfer_destination,
        variables={
            "conference_id": conference_id,
            "original_call_id": call.id,
            "call_reason": call.get_field("reason_for_calling"),
        },
    )


outbound_agent = guava.Agent(
    name="Nova",
    organization="Acme Corp",
    purpose="You are an outbound calling agent. You talk to customer service representatives on behalf of callers and brief them before a warm transfer.",
)


@outbound_agent.on_call_start
def on_outbound_call_start(call: guava.Call):
    call.set_task(
        "confirm_ready",
        f"""A caller is calling about {call.get_variable("call_reason")}. You are not talking to the caller - you are talking to a customer service representative.""",
        checklist=[
            "Inform the customer service agent of the caller's reason for calling.",
            "Determine if the service agent is ready for a transfer.",
        ],
    )


@outbound_agent.on_task_complete("confirm_ready")
def on_confirm_ready(call: guava.Call):
    conference_id = call.get_variable("conference_id")
    other_call = guava.RemoteCall(call.get_variable("original_call_id"))

    # Join the two calls.
    destination = f"conference:{conference_id}"
    other_call.transfer(destination, "The customer service representative is ready.")
    call.transfer(
        destination, "Transfer to the caller. Tell the rep that you are 'Patching in the caller.'"
    )


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--transfer-destination",
        metavar="PHONE_NUMBER",
        required=True,
        help="Number to warm transfer callers to.",
    )
    parser.add_argument(
        "--agent-number",
        metavar="PHONE_NUMBER",
        help="Number to listen on. Defaults to GUAVA_AGENT_NUMBER or your account's number.",
    )
    args = parser.parse_args()

    transfer_destination: str = args.transfer_destination
    agent_number: str = args.agent_number or get_agent_number()

    logging_utils.configure_logging()
    runner.listen_phone(agent, agent_number).run()
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.