A tool is an HTTP endpoint. The agent’s tool router chooses it. DotSIP then POSTs dotsip.tool-call to the endpoint stored on that tool. You define the tool under Tools.
Add a tool
- Open Tools.
- Set an ID, for example lookup-order. Use that ID in the agent’s tool list.
- Set the endpoint URL, a description the model can read, and a JSON parameters object. The shape of that object is on Tool parameters.
- Choose which rights IDs may use this tool.
- Save, then add the ID to the agent.
If the tool is not on the agent, or the call’s rights ID is not allowed for that tool, the model is not given the tool.
What DotSIP sends
The request is the same HTTP POST as a route, including User-Agent: DotSIP-Route/1. There is no built-in API key on the request. HTTPS verifies the certificate. HTTP does not.
{
"format": "dotsip.tool-call",
"format_version": 1,
"call_id": "call-id-from-dotsip",
"tool": "lookup-order",
"arguments": { "number": "DS-2026-000001" },
"caller": { "number": "+421900000000", "name": "", "rights": "right-1" }
}
The timeout starts at 5000 milliseconds. Answer with JSON. The field ok is required and must be a boolean. The model receives result when ok is true, and error when it is false.
{
"ok": true,
"result": { "status": "ready" },
"prompt": "The order is ready. Tell the caller the pickup desk."
}
ok true is success. false, or a missing ok, is an error result. result is not interpreted. The model reads it. rights on this body is ignored. prompt, when non-empty, is appended to the live instructions. agent, when non-empty, switches to that agent. Rights stay. The prompt on the same object is the new agent’s extra instruction. The new tool list is bound for the next dispatch. The live session that is already open is not given a replacement tool schema.
On the tool parameter schema, outside properties, x-dotsip-wait false means the model is given { "ok": true, "pending": true, "tool": "<id>" } immediately. When the endpoint returns, the outcome is appended as instructions and the model is asked to speak one short sentence if the caller should hear it. Omitted, or true, means the model waits.
A tool endpoint
PHP, Python, and Node.js answer a tool call the same way.
<?php
header("Content-Type: application/json");
$body = json_decode(file_get_contents("php://input"), true);
if (!is_array($body) || (isset($body["format"]) ? $body["format"] : "") !== "dotsip.tool-call") {
echo json_encode(array("ok" => false, "error" => "bad request"));
return;
}
$arguments = isset($body["arguments"]) && is_array($body["arguments"]) ? $body["arguments"] : array();
echo json_encode(array(
"ok" => true,
"result" => array("echo" => $arguments),
));
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.post("/tool")
def tool():
body = request.get_json(silent=True) or {}
if body.get("format") != "dotsip.tool-call":
return jsonify({"ok": False, "error": "bad request"})
arguments = body.get("arguments") if isinstance(body.get("arguments"), dict) else {}
return jsonify({"ok": True, "result": {"echo": arguments}})
const http = require("http");
http.createServer((req, res) => {
let raw = "";
req.on("data", (c) => { raw += c; });
req.on("end", () => {
let body = {};
try { body = JSON.parse(raw || "{}"); } catch (e) { body = {}; }
const send = (payload) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(payload));
};
if (body.format !== "dotsip.tool-call") {
send({ ok: false, error: "bad request" });
return;
}
const arguments = body.arguments && typeof body.arguments === "object" ? body.arguments : {};
send({ ok: true, result: { echo: arguments } });
});
}).listen(9099);
Keep the endpoint on a host the DotSIP machine can reach. A tool on 127.0.0.1 is reachable only from that same machine.