Call from an extension or a trunk

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

Extension 101 dials 102, or a trunk call arrives with a dialed number. DotSIP does not connect extensions by itself. Your server reads dialed and looks up your table. A group in the admin is only a named list. It does not place this call.

1. Records in DotSIP

  1. Under SIP phones, users 101 and 102, both enabled, each with a password.
  2. On phone 101, set the outbound route to route-tools if that field is on the phone. A dialed call from 101 then uses that route.
  3. Under SIP accounts, trunk id trunk. Leave inbound route empty if this example should fall through to route-tools.
  4. Under Routes, route-tools URL http://127.0.0.1:9099/route.

2. Your database

CREATE TABLE destinations (
  dialed TEXT PRIMARY KEY,
  kind TEXT NOT NULL,
  target TEXT NOT NULL
);
INSERT INTO destinations(dialed, kind, target) VALUES
  ('102', 'transfer', '102'),
  ('9000', 'dial', '+421900000000');

kind transfer uses target as the SIP phone user. kind dial uses target as the outside number. mode sequence tries one destination. parallel is the group-ring example.

3. What DotSIP posts

{
  "format": "dotsip.route",
  "format_version": 1,
  "direction": "inbound",
  "call_id": "c-7f3a",
  "sip_account": { "id": "trunk", "name": "trunk" },
  "sip_phone": null,
  "dialed": "102",
  "route": "route-tools",
  "caller": { "number": "101", "name": null, "rights": null, "groups": [] }
}

4. The server

<?php
$db = new PDO("sqlite:" . __DIR__ . "/operator.sqlite");
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec("CREATE TABLE IF NOT EXISTS destinations (dialed TEXT PRIMARY KEY, kind TEXT NOT NULL, target TEXT NOT NULL)");
$db->exec("INSERT OR IGNORE INTO destinations(dialed, kind, target) VALUES ('102', 'transfer', '102'), ('9000', 'dial', '+421900000000')");
$body = json_decode(file_get_contents("php://input"), true);
header("Content-Type: application/json");
if (!is_array($body) || (isset($body["format"]) ? $body["format"] : "") !== "dotsip.route") {
    http_response_code(400);
    echo json_encode(array("error" => "bad request"));
    return;
}
$dialed = isset($body["dialed"]) ? (string) $body["dialed"] : "";
$row = $db->query("SELECT kind, target FROM destinations WHERE dialed = " . $db->quote($dialed))->fetch(PDO::FETCH_ASSOC);
if (!$row) {
    echo json_encode(array("format" => "dotsip.route-result", "format_version" => 2, "commands" => array(array("do" => "hangup"))));
    return;
}
$step = $row["kind"] === "dial"
    ? array("do" => "dial", "number" => $row["target"], "mode" => "sequence", "timeout_ms" => 30000, "on_fail" => array(array("do" => "hangup")))
    : array("do" => "transfer", "phone" => $row["target"]);
echo json_encode(array(
    "format" => "dotsip.route-result",
    "format_version" => 2,
    "commands" => array(array("do" => "answer"), $step),
));

From phone 101, dial 102. The table says transfer to user 102, so that phone rings. Dial 9000 and the table says dial +421900000000 through the trunk, one number after another because mode is sequence. A dialed value with no row hangs up. Nothing in this server rings a group by itself.