HTTP API

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

An external program calls plain HTTP on the port of an active API key: http://<host>:<key port>. The admin site on port 9080 does not accept this token.

Create the key

  1. Open API in the admin.
  2. Create a key, set the port, and choose whether it listens on 127.0.0.1 (this computer) or 0.0.0.0 (anyone who can reach the port).
  3. Turn the key on. Copy the secret shown on the page. It stays there until you hide it.
  4. The external program stores that copy and sends it on every request.

After the secret is hidden, DotSIP cannot show it again. Only a fingerprint remains. Use New secret and copy the new value. Do not try to read the token back from the database.

Each key has its own port, bind, and firewall. Two active keys may share a bind and a port. Both tokens work there. A different bind gets its own listener. Turning off the last key on a bind and port stops that listener. The firewall then filters peers that reached the port.

Request

Content-Type is application/json. The body is one JSON object. A list is a GET with the same header and no body. Delete sends no body.

POST /v1/calls/originate HTTP/1.1
Host: 127.0.0.1:9090
Authorization: Bearer dsk_the-copied-secret
Content-Type: application/json

{"to":"+421901234567","extension":"101","sip_account":"trunk","behavior":"incoming"}

The same token may be sent as X-Api-Key instead of the Bearer line.

  • Missing or unknown token, or a token for another port or bind: 401 { "ok": false, "error": "unauthorized" }.
  • Token matches, but the peer is outside the firewall, or the key lists scopes and this operation is not one of them: 403 { "ok": false, "error": "forbidden" }.
  • Body larger than 64 KB: 413 { "ok": false, "error": "body" }.
  • Body is not a JSON object: 400 { "ok": false, "error": "json" }.
  • Unknown id: 404 { "ok": false, "error": "not found" }.
  • Runtime is not running, for originate: 503 { "ok": false, "error": "runtime" }.

Success bodies are { "ok": true, ... }. Create returns 201 and { "ok": true, "item": { ... } }. Originate returns 201 and { "ok": true, "call_id": "<id>" }. A duplicate id returns 409.

The firewall is a comma-separated list of CIDR blocks. An address without a slash is one host. An empty firewall allows every peer that reached the port. 127.0.0.1/32 allows only this computer.

An empty scope list allows every path. A listed scope allows only that operation: config:read for GET, config:write for changes to extensions, routes, groups, rights, and agents, tools:write for tools and routers, credentials:write for SIP accounts, calls:originate for dialing, and calls:control for listing and cancelling callback jobs. The same originate idempotency key with a different body returns 409.

GET /v1/callbacks returns open callback jobs in items. POST /v1/callbacks/{job_id}/cancel sets that job to cancelled and hangs up a leg that is already dialing. An unknown or finished job is 404.

Originate

POST /v1/calls/originate.

  • to (alias number) is required. Digits and + only, at least 3 characters after stripping.
  • One of extension (alias from) or sip_account (alias sip) is required. No handset has to be registered. The extension is the SIP From user. An omitted SIP channel uses the first enabled outgoing channel.
  • caller is required when there is no extension. It is the number placed in SIP From, with the same digit rules as to.
  • caller_id is an optional display name, at most 80 printable characters. Quotes and brackets are removed.
  • behavior (alias as) is incoming (default), outbound, or none. bypass, skip, and off mean none.
  • route (alias router) replaces the default route for that behaviour.
  • recording is the optional whole-call recording object from Calls, debug, access.

outbound without an extension is rejected. Identifiers are non-empty, at most 64 characters, and contain no spaces. A missing, disabled, or non-outgoing SIP channel is rejected before dialing. An explicit route that does not exist is rejected before dialing. incoming with no inbound route and no route-tools row is rejected before dialing.

After the far end answers, DotSIP POSTs the route decision unless the chosen route id is empty.

  • incoming with route set: that id, direction inbound. caller.number is to. dialed is the extension or caller.
  • incoming with route omitted: the SIP channel inbound route, else route-tools if that row exists.
  • outbound with route set: that id, direction outbound. caller.number is the extension. dialed is to. Groups are the extension’s groups.
  • outbound with route omitted: the extension’s outbound route. If that is empty, the call is not placed.
  • none with no route: no route POST. The answered call stays up.
  • none with route set: that id, direction inbound.
{
  "to": "+421901234567",
  "extension": "101",
  "sip_account": "trunk",
  "caller_id": "Reception",
  "behavior": "incoming"
}

Extensions

Passwords are never returned. has_password is a boolean. GET /v1/extensions returns items. GET /v1/extensions/{user} returns item with user, display_name, enabled, outbound_route, and has_password.

POST /v1/extensions creates. user is required. A duplicate is 409. display_name defaults to user. password is required when enabled is true. enabled defaults to true. An omitted outbound_route is empty. Optional ua_allow, expires_min, expires_max, and acl as a list of { "cidr", "action", "enabled" }.

On PATCH /v1/extensions/{user}, an omitted field keeps the current value, including acl. An empty acl list clears the rules. GET returns the effective acl without the password. An empty outbound_route clears it. An omitted password keeps the stored secret. POST /v1/extensions/{user}/enabled changes only enabled. DELETE /v1/extensions/{user} returns { "ok": true }. An enabled extension with no password is 400.

SIP channels

GET /v1/sip-accounts and GET /v1/sip-accounts/{account_id}. The password is not returned. Item fields include account_id, display_name, enabled, incoming, outgoing, registrar_uri, account_uri, username, realm, transport, local_port, transfer_uri_template, inbound_route, and has_password.

Create requires account_id, display_name, registrar_uri, account_uri, username, realm, and transfer_uri_template. account_id is lowercase letters, digits, and hyphens, max 64. URIs start with sip: or sips:. The transfer template contains only {extension}. Omitted optionals default to enabled, incoming, and outgoing true, transport udp, local port 5060, empty inbound route, and codecs PCMA/8000 and PCMU/8000. RTP defaults to 40000–40100.

PATCH does not rename the row. A new non-empty password replaces the secret. An empty password keeps it. Creating, changing, enabling, or deleting a SIP channel through this API reloads registration. The external program does not restart DotSIP for that. A validation failure is 400 with the validator message in error.

Routes, groups, and rights

/v1/routes: id is required on create. On patch the path id wins. scheme is https, http, wss, or ws. Anything else becomes https. url is required. verify_ssl omitted on create means true. A non-digit timeout_ms becomes 5000.

/v1/groups: id is required. all cannot be changed or deleted. label omitted becomes the id. members or phones is an array of extension users, or one comma-separated string, and replaces the membership. A built-in group cannot be deleted. The response includes builtin and members.

/v1/rights: id, label, and value. A live call sends the right id. value is stored on the row and is not the identifier sent with the call. is_default is returned and is not changed by this API.

Tools, routers, and agents

/v1/tools: id is the name the router must return. endpoint is required and is http, https, ws, or wss. description is text the router sees. parameters is a JSON object or a JSON string. Omitted on create becomes {}. It may contain x-dotsip-wait. rights is an array of right ids, or a comma-separated string, and replaces the tool’s rights. Deleting a tool removes it from agents.

/v1/routers creates an external router. tool-router-builtin cannot be created, changed, or deleted. Any other built-in row cannot be deleted. On a live call DotSIP uses the router's prompt and text model inside the voice session. It does not POST the router URL. scheme other than the four URL schemes is stored empty. verify_ssl omitted means false. The response includes kind (external or builtin) and builtin.

/v1/agents: the agent token is never returned. live_model defaults to gpt-live. router_id defaults to tool-router-builtin. tool_delivery is all or open. end_call_seconds is the farewell cap, 1 to 30, default 4. payload empty becomes {}. GET returns payload. An omitted PATCH keeps payload, prompt, live_model, router_id, tool_delivery, and end_call_seconds. An empty token on patch keeps the stored token. tools is an array of tool ids or a comma-separated string. On patch, omitted keeps the current tools. Present, even empty, replaces them.

Ids

Extension user, route id, group id, right id, tool id, router id, and agent id are 1 to 64 characters from letters, digits, ., _, and -. SIP channel ids are lowercase letters, digits, and hyphens, max 64.

Each catalog resource supports GET of the collection and of one id, POST to create, PATCH to update, and DELETE. POST /v1/extensions/{user}/enabled and POST /v1/sip-accounts/{account_id}/enabled take { "enabled": true } or { "enabled": false }.