Call Routing

HostDesk supports call routing operations: transferring calls to other numbers and putting callers on hold/resume. These are available via the API and as LLM tools the agent can invoke during a conversation.

Transfer a call

Transfer connects a third party to the existing call room. The original caller stays connected (warm transfer / conference-style).

API endpoint

POST /api/v1/calls/{room_name}/transfer
{
  "phone_number": "+14155559999",
  "announce": "Transferring you to a specialist..."
}

Response:

{
  "room_name": "echoai-out-abc123",
  "transfer_identity": "sip-transfer-14155559999-f8a2",
  "message": "Transferred to +14155559999. Both parties are now in room echoai-out-abc123."
}

As an LLM tool

The agent can call transfer_call during a conversation:

tools = [
    {
        "type": "function",
        "function": {
            "name": "transfer_call",
            "description": "Transfer the current call to another phone number",
            "parameters": {
                "type": "object",
                "properties": {
                    "phone_number": {
                        "type": "string",
                        "description": "E.164 phone number to transfer to"
                    },
                    "reason": {
                        "type": "string",
                        "description": "Reason for the transfer"
                    }
                },
                "required": ["phone_number"]
            }
        }
    }
]

When the LLM decides to transfer, HostDesk dials the target number via SIP and adds them to the room.

Hold and resume

Hold mutes the caller's audio track so they can't hear the agent (or plays hold music). Resume unmutes.

How it works

Hold/resume is implemented via LiveKit track muting:

  • Hold: The agent mutes the caller's audio subscription and optionally plays a hold audio track
  • Resume: The agent unmutes the caller's audio and stops the hold track

API usage

POST /api/v1/calls/{room_name}/hold
POST /api/v1/calls/{room_name}/resume

Both return 200 OK with the current room state.

As an LLM tool

tools = [
    {
        "type": "function",
        "function": {
            "name": "hold_call",
            "description": "Put the caller on hold",
            "parameters": {"type": "object", "properties": {}}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "resume_call",
            "description": "Take the caller off hold",
            "parameters": {"type": "object", "properties": {}}
        }
    }
]

Requirements

  • An active call in a LiveKit room
  • A provisioned outbound SIP trunk (for transfers)
  • A primary phone number configured as caller ID
  • Authorization: Bearer <api_key> header

Common patterns

Transfer to a department

@handler.register("transfer_to_support")
def transfer_to_support(room_name: str) -> str:
    client.calls.transfer(room_name, phone_number="+18005551234")
    return "Transferring you to our support team now."

Conditional transfer

The agent can decide to transfer based on the conversation:

User: "I need to speak to a real person." Agent: "Of course, let me transfer you to our team." (Agent calls transfer_call tool)