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#
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 themYou 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 Tools → Create 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#
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{
"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#
| Field | Required | Notes |
|---|---|---|
name | optional | Friendly name shown in the dashboard. |
serverUrl | yes | Your directory server URL. OnCore's tool-proxy forwards to it and injects your tenant secret as x-sadie-core-secret. |
body.type | yes | "dynamicTransfer". ("function" and "sendDtmf" are the other tool types — see Tools.) |
body.function | yes | OpenAI-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.defaultMode | optional | "cold" | "warm" | "warm-return". Fallback when your server doesn't return a mode. |
body.messages | optional | Tool messages, e.g. a request-start filler spoken during the lookup. |
instruction | yes | A title, a description, and at least one step (title, action, wait_for_response). |
Your directory server contract#
Through the tool-proxy your server receives:
{ "call_id": "<id>", "arguments": { "firstName": "Alice" }, "metadata": { "language": "en" } }with header x-sadie-core-secret: <your tenant secret>. Respond with exactly one of:
{ "status": "resolved", "destination": "+15551234567", "mode": "warm",
"holdMessage": "Connecting you to billing now.", "skipHoldMessage": false }{ "status": "ambiguous", "candidates": [
{ "id": "c1", "label": "John Smith — Billing" },
{ "id": "c2", "label": "John Doe — Front Desk" } ] }{ "status": "not_found", "message": "No contact by that name." }modeis optional per match; precedence is servermode→defaultMode→"cold".candidatescarry labels only — never phone numbers. The number appears only inresolved.destination.
Errors#
| Status | Cause |
|---|---|
400 | Body fails validation — check body.type, function, and that instruction has a title, description, and at least one step. |
401 | Missing or invalid Authorization: ApiKey <token> header. |
500 | Transient OnCore failure — retry once. |
Next steps#
- Tools — the general tool model and
function-type tools. - Assistants — attaching tools via
toolIdsand configuringtransferMode.