Logo
Search
API Docs

Dynamic Transfer Routing

Tools

Support Escalation: Dynamic Transfer Routing

Overview

A static transferCall destination works well when you always want the same phone number reached — see Call Forwarding & Transfer Modes for that setup. This page covers a more advanced pattern: a support-escalation system where the assistant decides that a call needs escalating, but the actual destination is resolved by your own server at the moment of transfer — based on customer tier, issue complexity, or live agent availability. This is the same dynamic destination concept introduced on the Squad Routing page, applied here to human/live-agent transfers via transferCall rather than assistant-to-assistant handoffs.


The Escalation Tool: An Empty destinations Array

The key to a dynamic escalation tool is creating a transferCall tool with an empty destinations array. This empty array is not an oversight — it's the signal that tells the platform the real destination will be resolved at runtime via a webhook, rather than pulled from a fixed number configured on the tool itself:

curl --location 'https://api.sulus.ai/tool/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--data '{
    "type": "transferCall",
    "destinations": [],
    "function": {
        "name": "escalateToSupport",
        "description": "Escalate calls to the appropriate support specialist based on customer tier and issue complexity",
        "parameters": {
            "type": "object",
            "properties": {
                "issue_category": {
                    "type": "string",
                    "description": "Category of customer issue",
                    "enum": ["technical", "billing", "account", "product"]
                },
                "complexity_level": {
                    "type": "string",
                    "description": "Issue complexity level",
                    "enum": ["basic", "intermediate", "advanced", "critical"]
                },
                "customer_context": {
                    "type": "string",
                    "description": "Relevant customer information for routing"
                },
                "escalation_reason": {
                    "type": "string",
                    "description": "Why this needs escalation rather than self-service"
                }
            },
            "required": ["issue_category", "complexity_level"]
        }
    }
}'

See Function Tool Parameter Schema for the general rules governing this parameters object (the required array, strict validation, and maxTokens).


Parameter Schema Reference

The escalation tool captures four fields the model fills in based on the conversation, two required and two optional:

ParameterRequiredValues / Purpose
issue_categoryYesEnum: technical, billing, account, product
complexity_levelYesEnum: basic, intermediate, advanced, critical
customer_contextNoFree-text relevant customer information used to help routing (e.g. account tier, prior interactions)
escalation_reasonNoFree-text explanation of why this needs escalation rather than self-service

These four fields are what your webhook receives and reasons over when deciding the live destination — write your system prompt so the model actually populates customer_context and escalation_reason with useful detail rather than leaving them blank.


System Prompt: Try Self-Service First

The escalation tool works best when the assistant is explicitly instructed to attempt self-service before reaching for it:

{
  "role": "system",
  "content": "You are TechCorp's intelligent customer support assistant. Try to resolve simple issues yourself first using the knowledge base. For complex issues, or when the customer explicitly asks for a human, escalate using the escalateToSupport function. Set issue_category and complexity_level based on the conversation, and fill in customer_context and escalation_reason with anything that will help the receiving specialist pick up quickly."
}

As with any tool, defining it isn't enough on its own — the system prompt has to name it and describe when to use it, the same requirement covered on the Query Tool Setup and Function Tool Parameter Schema pages.


Enabling the transfer-destination-request Server Event

An empty destinations array only works if your assistant is also configured to send the transfer-destination-request server event — enable it in your assistant's server event settings, alongside your webhook serverUrl. Without this enabled, the platform has nowhere to ask for the live destination once the tool is invoked.

When the assistant calls escalateToSupport, your server receives a transfer-destination-request event carrying the call and the tool call arguments. Your server must respond with a destination, resolved using whatever logic you want — a CRM lookup, an agent-presence check, a priority queue lookup, and so on:

{
  "destination": { "type": "number", "number": "+11234567890" },
  "message": { "type": "request-start", "message": "Transferring you to the right specialist now." }
}

A SIP URI destination can be returned the same way with "type": "sip" and a sipUri field instead of a number. See Call Forwarding & Transfer Modes for the full mechanics of this webhook exchange, including what happens if the assistant's tool call already carries a destination (the webhook is skipped entirely in that case).


Four Capabilities This Pattern Supports

Because the destination is resolved by your own server rather than fixed in the tool config, this pattern supports:

  • Customer-tier prioritization — route VIP or high-value customers to a dedicated queue or specialist based on customer_context.
  • Issue-complexity-based routing — send critical or advanced issues to senior support, while basic or intermediate issues go to general support.
  • Real-time agent-availability matching — check which agents or queues are actually free at the moment of the request, rather than routing to a number that might be unstaffed.
  • Context preservation across the handoff — use escalation_reason and customer_context to pass a spoken summary to the receiving party via the webhook's message field, so the human agent isn't starting from zero.

Debugging: endedReason and the Transfer-Support Matrix

If a transfer drops immediately after being initiated, the first thing to check is the call's endedReason field — it tells you whether the drop was a transfer-specific failure or something else entirely. See Ended Reason: Complete Reference for the full list of transfer-related values (for example, the call.in-progress.error-transfer-failed family).

A very common cause of an immediate drop is attempting a transfer combination that simply isn't supported at the signaling level. This matrix, also documented in full on Call Forwarding & Transfer Modes, is the first thing to check:

FromToSupported
Phone callPhone numberYes
Web callPhone numberNo – the call will always drop
SIP callSIP numberYes
SIP callPhone numberYes

If the combination is supported and the drop still happens immediately, check that your webhook actually responded to the transfer-destination-request event within its timeout window, and that the destination payload matches the expected shape — a malformed or missing destination response is the next most common cause of an immediate drop on an otherwise-valid transfer path.