Call Transfer

View as Markdown

Call Transfer

There are two ways an agent transfers a live call. Pick by how you built the agent.

You built the agent as…Transfer is configured by…
A single-prompt agent (prompt + tools)A transfer_call tool on the agent — set it from code with AgentTools
An agent crew (custom node code / custom LLM)Your node emits a transfer event (SDKAgentTransferConversationEvent)

Both bridge the call to the number you give, optionally with hold music while it connects.

Cold vs warm

  • Cold transfer (cold_transfer) — a direct connect. The destination is dialed and the caller is bridged straight through, with no debrief. There is no hold music, whisper, or three-way step (it connects directly). Use for “send them to the front desk”.
  • Warm transfer (warm_transfer) — the agent debriefs the destination before bridging. This handover window is where the extra options apply: on-hold music, a whisper message to the destination only, and a three-way message to both parties.

Hold music applies to warm transfer, not cold. Cold is a direct connect with no window for audio, so on_hold_music has no effect on it. If a transfer connects but sounds blank, it is almost always a cold transfer with on_hold_music set — switch to warm_transfer.

on_hold_music values: ringtone, relaxing_sound, uplifting_beats, none.

Single-prompt agents

Agent config lives in the branch/revision versioning model, and serving reads the live branch’s head revision. AgentTools handles that whole flow for you (open a draft → publish → make live), so a tool you add takes effect on the next call.

1from smallestai.atoms.helpers import AgentTools
2
3tools = AgentTools(api_key="sk_...") # or SMALLEST_API_KEY
4
5tools.add_transfer_call(
6 "AGENT_ID",
7 number="+15551234567",
8 transfer_type="cold_transfer", # or "warm_transfer"
9 on_hold_music="relaxing_sound", # warm only
10)

Inspect or remove:

1for t in tools.get_tools("AGENT_ID"):
2 print(t.type, t.name)
3
4tools.remove_tool("AGENT_ID", "transfer_call")

Do not write the legacy workflow document (PATCH /workflow/{id}) to set tools — under the branch model, serving ignores it on live calls, and the v1 drafts/versions endpoints are deprecated. Use AgentTools (or the branch API directly).

Agent crew

In a crew, the transfer is code. From a node (for example a @function_tool), emit SDKAgentTransferConversationEvent:

1from smallestai.atoms.crew.events import (
2 SDKAgentTransferConversationEvent,
3 TransferOption,
4 TransferOptionType,
5)
6
7@function_tool(name="transfer_call")
8async def transfer_call(self) -> None:
9 await self.send_event(
10 SDKAgentTransferConversationEvent(
11 transfer_call_number="+15551234567",
12 transfer_options=TransferOption(type=TransferOptionType.COLD_TRANSFER),
13 on_hold_music="relaxing_sound", # warm only; optional
14 )
15 )

Requires smallestai>=5.4.0. In that release on_hold_music is optional (defaults to None), and a crew agent reliably has the caller’s latest turn in context. Earlier versions could leave the context empty on the first turn, so the agent would greet once and then stay silent (and so never reach the transfer).

Steps to get a transfer working end to end

1

Use a real, reachable destination number

E.164 (for example +15551234567). The transfer only completes when the destination answers — a number that goes to voicemail or does not pick up shows up as no_answer / timeout on the transfer leg.

2

Choose cold or warm

transfer_type (single-prompt) or TransferOptionType (crew). Set on_hold_music if you want audio during a warm handover.

3

Tell the LLM when to transfer

The tool only fires if the model calls it. Put a clear instruction in the prompt, e.g. “If the caller asks for a human, an agent, or a specialist, call the transfer_call tool immediately.”

4

Verify from the call logs

Check the parent call and the transfer leg (client.atoms.calls.get(...)). A transfer leg with status: no_answer fired correctly but the destination did not pick up.

Troubleshooting

The transfer_call tool is not on the live config. For single-prompt agents, set it with AgentTools (the legacy workflow doc does not take effect under the branch model).

The transfer fired but the destination did not answer (or the caller hung up first). Use a number a human or agent will pick up.

You are using a cold transfer (direct connect, no music). For hold music during the handover, use a warm transfer and set on_hold_music.

Upgrade to smallestai>=5.4.0. Earlier versions could leave the crew context empty on a turn, so the LLM had nothing to answer and stayed silent.