Hand off to another agent

Install DotSIP, open the web admin, connect real SIP, and decide each call from your own HTTP API.

The first agent finds the patient and a free time, then calls handoff. Your server returns agent and a new prompt. Rights stay the ones from the route. The new tool list is bound for the next dispatch. The live session that is already open is not given a replacement tool schema, so it keeps calling the tools from the start of the call.

1. Records in DotSIP

  1. Agents agent-desk and agent-confirm. agent-desk tools: find_patient, free_slots, handoff. Router tool-router-builtin. Rights on the route: right-1.
  2. All three tools use endpoint http://127.0.0.1:9099/tool and rights right-1.
  3. agent-confirm may list other tools in the admin. This call does not push that list into the open voice session.
  4. route-tools URL http://127.0.0.1:9099/route.
  5. Under SIP accounts, the trunk that receives the call. Set its inbound route to route-tools, or leave that field empty so the call uses route-tools.

2. Your database

CREATE TABLE handoff (id TEXT PRIMARY KEY, next_agent TEXT NOT NULL, prompt TEXT NOT NULL);
INSERT INTO handoff(id, next_agent, prompt) VALUES
  ('desk', 'agent-confirm', 'Confirm 28 Sep at 09:20 for patient p-17. Do not book anything else.');

3. What DotSIP posts for handoff

{
  "format": "dotsip.tool-call",
  "format_version": 1,
  "call_id": "c-7f3a",
  "tool": "handoff",
  "arguments": { "day": "2026-09-28", "time": "09:20", "patient_id": "p-17" },
  "caller": { "number": "+421901234567", "name": null, "rights": "right-1" }
}

4. The server

The route starts agent-desk. find_patient and free_slots read the same tables as the appointment example. handoff reads the handoff row and returns agent plus prompt. Do not send rights. It is ignored.

<?php
$db = new PDO("sqlite:" . __DIR__ . "/operator.sqlite");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec("CREATE TABLE IF NOT EXISTS handoff (id TEXT PRIMARY KEY, next_agent TEXT NOT NULL, prompt TEXT NOT NULL)");
$db->exec("INSERT OR IGNORE INTO handoff(id, next_agent, prompt) VALUES ('desk', 'agent-confirm', 'Confirm 28 Sep at 09:20 for patient p-17. Do not book anything else.')");
$body = json_decode(file_get_contents("php://input"), true);
header("Content-Type: application/json");
$format = is_array($body) && isset($body["format"]) ? $body["format"] : "";
if ($format === "dotsip.route") {
    echo json_encode(array("format" => "dotsip.route-result", "format_version" => 2, "commands" => array(
        array("do" => "answer"),
        array("do" => "agent", "agent" => "agent-desk", "rights" => "right-1", "prompt" => "Find the patient and a free time. When they agree, call handoff."),
    )));
    return;
}
if ($format === "dotsip.tool-call" && (isset($body["tool"]) ? $body["tool"] : "") === "handoff") {
    $row = $db->query("SELECT next_agent, prompt FROM handoff WHERE id = 'desk'")->fetch(PDO::FETCH_ASSOC);
    echo json_encode(array(
        "ok" => true,
        "result" => array("day" => "2026-09-28", "time" => "09:20"),
        "prompt" => $row["prompt"],
        "agent" => $row["next_agent"],
    ));
    return;
}
if ($format === "dotsip.tool-call") {
    echo json_encode(array("ok" => true, "result" => array("patient_id" => "p-17", "slots" => array("09:20"))));
    return;
}
http_response_code(400);
echo json_encode(array("ok" => false, "error" => "bad request"));

After handoff, the open voice session still offers find_patient, free_slots, and handoff. It does not receive a new tool schema. The new prompt is the text from the handoff table.