Calls
Transfers
The three transfer modes (cold, warm, and warm-return); what each one sounds like to the caller and the person picking up, and when to choose which.
When a caller needs a person, the assistant hands the call over. How it hands over is the difference between someone answering blind and someone answering prepared, and between a missed hand-off ringing out and the caller coming back to a voice that explains what happened.
There are three modes. This page covers all three. For routing to a named person or department resolved at call time, see Dynamic transfer.
The three modes at a glance#
| Mode | The person picking up hears | If nobody answers | Use it for |
|---|---|---|---|
| Cold | The caller, immediately | The call has already left OnCore; your phone system handles it | Always-staffed lines, IVRs, overflow numbers |
| Warm | A spoken briefing, then the caller | The call has left OnCore; your phone system handles it | Named people, anything where context matters |
| Warm-return | A spoken briefing, then the caller | The caller returns to the assistant, which explains and continues | Lines that are not always staffed |
The choice is really two questions. Does the person picking up need context? That is cold versus warm. What should happen if nobody picks up? That is warm versus warm-return.
Cold transfer#
The blind hand-off, and the default. OnCore connects the caller to the destination and steps out of the call entirely.
caller ──► assistant ──► destination
└── OnCore leaves the callThe person answering gets a ringing phone and a stranger on the line. They have no idea who is calling or why, so the caller repeats everything they already told the assistant.
That is a real cost, but cold is the right choice more often than it sounds:
- The destination is a system, not a person: an IVR, a queue, a voicemail box. A briefing spoken to a menu is wasted.
- The line is always staffed: a call centre with agents waiting does not need warm-return, and the extra seconds before connection are a worse trade than the missing context.
- You want the shortest possible path: cold is the fastest hand-off available.
Once a cold transfer connects, the call is out of OnCore's hands. If it rings out, hits voicemail, or drops, that is your telephony system's behavior, not something OnCore can recover from.
Warm transfer#
Before the two are connected, OnCore speaks a short briefing (a whisper) to the person picking up: who is calling and what they need, drawn from the conversation so far.
caller ──► assistant ──► destination rings
(holding) └── whisper: "Call from Maria Santos about
a billing question on invoice 4471."
└── caller joinsThe caller does not hear the whisper. The person picking up answers already knowing the situation, and the caller does not have to start over.
Two things worth knowing:
- The whisper is spoken in the language of the call. A Spanish-speaking caller produces a Spanish briefing.
- You can delay it.
warmWhisperDelaySeconds(0 to 30, default0) pauses before the whisper plays, which helps when the destination has a greeting or an answering system that talks first and would otherwise talk over it.
Like cold, once the caller is connected the call has left OnCore. Warm improves the hand-off; it does not give you a safety net.
Warm-return#
Everything warm does, plus the safety net: if the hand-off does not succeed, the caller comes back to the assistant instead of ringing out into nothing.
caller ──► assistant ──► destination rings
(holding)
├── answered ──► caller connected, OnCore leaves
└── no answer ──► caller returns to the assistant
"I could not reach anyone just now…"
└── conversation continuesThe caller hears wording you control, and the conversation resumes, so the assistant can take a message, offer a callback, or try a different destination, rather than leaving the caller listening to a dead line.
This is the mode to reach for on any line that is not guaranteed to be staffed: a single named person, a small team, anything out of hours, anything where "nobody picked up" is a realistic outcome rather than an edge case.
What the caller hears on return#
Set warmReturnMessage on the assistant (up to 2000 characters). Send "" to restore the built-in wording.
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"warmReturnMessage": "Sorry, nobody on the billing team is free right now. I can take a message and have them call you back, or I can try our general line. Which would you prefer?"
}'Write it as an opening, not an apology. The caller is still on the phone and the assistant is still able to help, so the message should hand them a next step rather than a dead end.
Voicemail returns the caller, but still reports as answered
Two things are true at once here, and they look contradictory until you separate them:
- The caller does come back. Warm-return runs answering-machine detection on the destination by default. When a machine or a fax picks up, the caller is pulled back out of the hand-off and returned to the assistant, exactly as if nobody had answered.
supervisor_answeredwill still betrue. That field on the end-of-call report records whether the call connected, and a voicemail box connects. It is not a record of whether a person picked up.
So the behaviour is what you want, but the field is not the signal you want.
Detecting a missed hand-off
Do not branch on supervisor_answered alone to decide whether a human took the call; on
warm-return it reads true for voicemail. Machine detection resolves a moment after the
connection, so the return happens asynchronously; treat a returned caller as the signal
that the hand-off failed.
Confirm the exact detection outcomes you can rely on with the platform team before building alerting on top of this.
Turning machine detection off#
Detection is on for every warm-return transfer unless the assistant opts out:
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transferSettings": { "amd_detection": false }
}'transferSettings is deep-merged, so sending one knob leaves the others as
stored. Send null to clear the whole document.
In the dashboard it is the Answering Machine Detection switch under Call Settings. It only appears when Transfer Mode is set to Warm Transfer (Return), since that is the only mode the setting affects.
Off does not mean "the same, without detection"
With detection off the caller is not returned when a machine answers. Twilio reports voicemail as an ordinary answer, the bridge forms, and the caller is left listening to the greeting until the call ends. Only turn this off for a destination where being connected to whatever answers is the outcome you want.
Send true, or omit the key, to go back to the default.
Caller ID on a transfer#
By default the person answering a transfer sees the customer's phone number, in every mode. If the customer withheld their number, they see your assistant's number instead.
You can have them see your assistant's number on every transfer. That is the number the customer dialled, so your team can tell a transfer from the business line and answer it as one, instead of picking up an unknown number.
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transferSettings": { "use_assistant_number": true }
}'It applies to cold, warm and warm-return transfers alike. transferSettings deep-merges, so turning this on leaves a stored amd_detection as it was.
In the dashboard it is the Show Assistant Number on Transfers switch under Call Settings, shown for every transfer mode. The switch and the API field set the same value.
Your team no longer sees who is calling
With use_assistant_number on, the caller ID shows your assistant's number, not the customer's. On a warm or warm-return transfer the whisper still briefs the person answering on what the caller needs, including their name if they gave one. A cold transfer has no whisper, so they pick up knowing only that the call came through the assistant. The customer's number is still on the end-of-call report as customer_number.
Send false, or omit the key, to go back to showing the customer's number.
Choosing a mode#
Is the destination a person?
├── No (IVR, queue, voicemail) ──► cold
└── Yes
└── Is the line always staffed?
├── Yes (call centre, rota) ──► smart-summary
└── No (named person, small team) ──► warm-returnIf you are unsure, warm-return is the safest default for a human destination. Its only cost over a plain warm transfer is the return path, which only runs when the hand-off has already failed.
Configuring it#
Transfer mode is set on the assistant and applies to its escalations.
# Warm-return: brief the destination, and bring the caller back if nobody answers.
curl -X PATCH https://core-api.heysadie.ai/assistants/0195f1e2-0000-0000-0000-000000000000 \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transferMode": "warm-return",
"warmWhisperDelaySeconds": 2,
"settings": {
"warmReturnMessage": "They could not pick up just now, but I can take a message instead."
}
}'For a plain warm transfer, send "transferMode": "smart-summary" and omit
warmReturnMessage. The caller is connected once the briefing finishes and does not come
back if the line rings out. For cold, send "transferMode": "cold-transfer".
| Field | Type | Notes |
|---|---|---|
transferMode | string | cold-transfer, smart-summary, or warm-return. See the note below. Defaults to cold-transfer. |
warmWhisperDelaySeconds | integer | 0 to 30. Pause before the whisper plays. Defaults to 0. Stored under settings.warmWhisperDelaySeconds. |
warmReturnMessage | string | Max 2000 characters. What the caller hears on return. Stored under settings.warmReturnMessage; "" restores the default. |
Only three values are accepted
Only these values produce a warm transfer:
| Value | Behaviour |
|---|---|
cold-transfer | Connect the caller straight through. This is the default. |
smart-summary | Brief the destination, then connect the caller. |
warm-return | Brief the destination; return the caller if nobody answers. |
Anything else is rejected with a 400 naming these three values. The match is exact and
case-sensitive, so "warm", "warm-transfer" and "Warm-Return" are all errors.
Historically these were accepted, stored, and then performed a cold transfer on every call with no error anywhere. If you are debugging an older assistant whose warm transfers appear to do nothing, check this value first.
In the dashboard, pick the mode from the Transfer Mode dropdown under Call Settings instead; it writes these values for you, and hovering an option describes what it does.
Controlling what the briefing says#
By default the assistant composes the briefing itself from the conversation so far. You can
take that over: point the assistant's whisperSummaryPromptId at a
prompt of type whisper_summary and it decides what the
person picking up hears.
That is where to put anything your team needs stated in a fixed order (account number first, then the reason, then what the caller has already been told) rather than trying to shape it from the main prompt, which is written for the caller.
| Setting | Effect |
|---|---|
whisperSummaryPromptId | The prompt that writes the briefing. null restores the built-in behaviour. |
warmWhisperDelaySeconds | Pause before the briefing starts playing, 0 to 30. Useful when the destination's phone system answers with its own greeting. |
The briefing is spoken in the language of the call, so a caller handled in French produces a French briefing without extra configuration.
Per-destination modes#
With dynamic transfer, your directory server can choose the mode per destination, so the same assistant can hand off cold to a queue and warm-return to a named person:
{ "status": "resolved", "destination": "+15551234567", "mode": "warm-return" }Precedence is server mode → defaultMode → "cold".
When a transfer happens#
Setting a mode on its own transfers nothing. A transfer reason is the rule that decides when to hand off and where to send each kind of request: a billing question to one number, a complaint to another. Reasons decide whether a hand-off happens; the mode decides how it is performed.
So a working transfer is two calls: one to set the mode on the assistant, one to give it at least one reason.
curl -X POST https://core-api.heysadie.ai/transfer-reasons \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"assistantId": "0195f1e2-0000-0000-0000-000000000000",
"name": "Speak to a person",
"reason": "The caller asks for a human, or is unhappy and wants to escalate.",
"message": "Of course, let me put you through to someone now.",
"phoneNumber": "+15551234567",
"properties": {}
}'reason is what the assistant matches against, so write it the way a caller behaves rather
than as a category label. message is spoken before the hand-off begins. Add one reason per
destination (a billing line, an escalation line) and the assistant picks between them.
Full field reference, including waitForResponse and per-reason extensions:
Transfer reasons.
Seeing the outcome#
Every attempted transfer appears on the end-of-call report:
| Field | Meaning |
|---|---|
transfer_reason_id | Set whenever a transfer was attempted, regardless of how the call ended |
supervisor_answered | true answered, false unanswered (no-answer, busy, failed, canceled), null when there was no transfer |
supervisor_answered: false is the signal worth acting on: it means a caller wanted a person and did not reach one. Alert the team, queue a callback, or log it against the destination so you can see which numbers keep missing calls.
function onCallReport(event) {
if (event.transfer_reason_id && event.supervisor_answered === false) {
// A caller asked for a human and nobody answered.
void alertTeam({
callId: event.call_id,
caller: event.customer_number,
reason: event.transfer_reason_id,
});
}
}Transfers do not work on WhatsApp calls
transferCall and dynamicTransfer tools are removed before a WhatsApp call starts. The
assistant is not told they are missing, so it simply has no way to hand the caller on; it
will keep trying to help rather than escalating.
This is easy to miss because it is source-dependent, not configuration-dependent: the same assistant transfers correctly on a phone call and silently cannot on WhatsApp. If an assistant handles both, give it a fallback the caller can use: a number to dial, or a capture form that takes a callback request.
Limits & FAQ#
- Transfers forward to any reachable phone number; an extension can be dialled automatically after the call connects.
- The dial timeout for a transfer attempt is configurable between 1 and 60 seconds.
Does the person answering see the caller's number? By default, yes. Transfers keep the customer's phone number, so your team's caller ID shows the customer, not OnCore. A customer who withholds their number shows as your assistant's number. Set use_assistant_number to show your assistant's number on every transfer instead; see Caller ID on a transfer.
Can one assistant transfer to different places? Yes. Add as many transfer reasons as you have scenarios, each with its own trigger and destination. Alternatively, use dynamic transfer to resolve the destination from your directory at call time.
Common problems#
The whisper talks over the destination's greeting. Raise warmWhisperDelaySeconds. Two to three seconds is usually enough to clear an automated greeting.
Callers report dead air after asking for a human. Almost always a cold transfer to an unstaffed line: the call left OnCore and rang out. Switch that destination to warm-return.
supervisor_answered is null on a call you expected to transfer. No transfer was attempted. Check that a transfer reason actually matches the situation, and that its description tells the model when to use it.
The transfer never fires at all. Work through the tool and prompt checks in Calls and assistant behavior.
Next steps#
- Dynamic transfer: resolving a destination by name at call time.
- Transfer Reasons API: defining when and where.
- Assistants: the full field reference.
- End-of-call report: transfer outcome fields.