Skip to content

Guides

Dynamic transfer

Let callers be transferred by name, with the destination resolved at call time by your own directory server.

A dynamic transfer tool lets a caller ask to be connected to a person or department by name, with the destination resolved at call time by your directory server — instead of baking a fixed reason-to-number list into the assistant. The phone number never reaches the LLM, the transcript, or spoken output.

How it works#

text
Caller: "transfer me to billing"
  → agent collects your params (e.g. firstName) and POSTs them to your server
    (through OnCore's tool-proxy, which injects your tenant secret)
  → your server replies: resolved | ambiguous | not_found
  → resolved  → agent auto-fires the transfer to the returned number
    ambiguous → agent reads the candidate labels back and asks which one
    not_found → agent tells the caller it couldn't find them

You author the tool once (below). Routing logic lives on your server.

Create from the OnCore dashboard#

No code needed — the dashboard builds the same payload for you:

Open the tool builder#

Go to ToolsCreate Tool.

Choose the type#

Set Tool Type to Dynamic Transfer.

Pick a default transfer mode#

Default Transfer Mode — pick Cold / Warm / Warm-return, or leave None (agent decides).

Define the function and server#

Fill in the function (name, description, and parameters — e.g. a firstName string) and the Server URL (your directory server).

Add instructions#

Add the Instructions (title, description, and at least one step).

Create and attach#

Click Create Tool, then attach it to your assistant.

For programmatic or bulk creation, use the API below.

Create the tool — POST /tools#

bash
curl -X POST https://core-api.heysadie.ai/tools \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d @tool.json   # the JSON body below
tool.json
{
  "name": "Transfer to contact",
  "serverUrl": "https://your-directory-server.example.com/find-destination",
  "body": {
    "type": "dynamicTransfer",
    "function": {
      "name": "transferToContact",
      "description": "Look up a person by name in the company directory and transfer the caller to them. Use whenever the caller asks to be connected or transferred to a specific person or department.",
      "parameters": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "First name of the contact the caller wants to reach"
          }
        },
        "required": ["firstName"]
      }
    },
    "defaultMode": "cold",
    "messages": [
      { "type": "request-start", "mode": "static", "content": "One moment, connecting you now." }
    ]
  },
  "instruction": {
    "title": "Transfer caller to a named contact",
    "description": "How to use transferToContact to connect the caller to the right person.",
    "steps": [
      {
        "title": "Get the contact name",
        "action": "Ask who the caller wants to reach and capture their first name.",
        "wait_for_response": true
      },
      {
        "title": "Look up and connect",
        "action": "Call transferToContact with the first name. If several matches come back, read the options to the caller and ask which one, then call it again. If there's no match, tell the caller you couldn't find that person.",
        "wait_for_response": false
      }
    ]
  }
}

Then attach the tool to an assistant (in the dashboard, or via the assistant API).

Avoid two transfer tools

For a clean setup, clear the assistant's transfer_reasons so the classic transferCall tool isn't also offered — otherwise the model has two transfer tools and may pick the wrong one.

Request fields#

FieldRequiredNotes
nameoptionalFriendly name shown in the dashboard.
serverUrlyesYour directory server URL. OnCore's tool-proxy forwards to it and injects your tenant secret as x-sadie-core-secret.
body.typeyes"dynamicTransfer". ("function" and "sendDtmf" are the other tool types — see Tools.)
body.functionyesOpenAI-style function: name, description, and parameters (type: "object"). You define the params freely — firstName is just an example; add lastName, department, etc. as your server needs. The description drives when the model calls the tool — be explicit.
body.defaultModeoptional"cold" | "warm" | "warm-return". Fallback when your server doesn't return a mode.
body.messagesoptionalTool messages, e.g. a request-start filler spoken during the lookup.
instructionyesA title, a description, and at least one step (title, action, wait_for_response).

Your directory server contract#

Through the tool-proxy your server receives:

Request to your server
{ "call_id": "<id>", "arguments": { "firstName": "Alice" }, "metadata": { "language": "en" } }

with header x-sadie-core-secret: <your tenant secret>. Respond with exactly one of:

1. Single match — agent transfers (number stays server-side)
{ "status": "resolved", "destination": "+15551234567", "mode": "warm",
  "holdMessage": "Connecting you to billing now.", "skipHoldMessage": false }
2. Several matches — agent reads labels back, asks which
{ "status": "ambiguous", "candidates": [
  { "id": "c1", "label": "John Smith — Billing" },
  { "id": "c2", "label": "John Doe — Front Desk" } ] }
3. No match
{ "status": "not_found", "message": "No contact by that name." }
  • mode is optional per match; precedence is server modedefaultMode"cold".
  • candidates carry labels only — never phone numbers. The number appears only in resolved.destination.

Errors#

StatusCause
400Body fails validation — check body.type, function, and that instruction has a title, description, and at least one step.
401Missing or invalid Authorization: ApiKey <token> header.
500Transient OnCore failure — retry once.

Next steps#

  • Tools — the general tool model and function-type tools.
  • Assistants — attaching tools via toolIds and configuring transferMode.