API v1 — stable

Exit Server Tunnel API Reference

Create and remove WireGuard-over-TCP users on your own exit server, from your own system. With auto-apply enabled, the peer is also created on your MikroTik router, with nobody running a command by hand.

https://servernet.cloud/en/developers/tunnel · 2026/08/25

GET /api/v1/tunnel/servers exit servers with a TCP tunnel tunnel:read
GET /api/v1/tunnel/{service}/accounts users and their state tunnel:read
POST /api/v1/tunnel/{service}/accounts create user — private key returned once tunnel:write
DELETE /api/v1/tunnel/{service}/accounts/{name} delete user tunnel:write
GET /api/v1/tunnel/{service}/agent auto-apply status tunnel:read
POST /api/v1/tunnel/{service}/agent enable auto-apply — token returned once tunnel:write

1Prerequisites and authentication

This interface only works for exit servers that have a TCP tunnel configured. A server without such a profile does not appear in the listing at all, so you never have to know which server is eligible: the listing itself is the answer.

  1. Open the Security page in your client panel and issue an API token.
  2. Tick the two scopes tunnel:read and tunnel:write.
  3. If needed, set an IP allowlist on that token so it only works from your own server.
  4. The token is shown once; store it right there.
curl -H "Authorization: Bearer sn_xxxxxxxx" \
     https://servernet.cloud/api/v1/tunnel/servers
The IP allowlist lives on the token, not on the account. Enabling the account level IP rules would also lock you out of your own browser.

2Find your service id

Every later path needs a numeric id. Read it from this call and keep it in your code.

GET https://servernet.cloud/api/v1/tunnel/servers

{"ok": true, "data": [{
  "service_id": 49,
  "name": "blackwood-vip-1",
  "status": "active", "writable": true,
  "host": "sn-571100.servernet.cloud", "port": 8443,
  "subnet": "10.77.0.0/24", "next_ip": "10.77.0.5",
  "accounts": 7, "max": 100,
  "agent": {"installed": true, "alive": true,
            "last_seen_at": "2026-08-24T08:16:18+00:00",
            "pending_jobs": 0}
}]}
service_idthe id that replaces {service} in every later path
hostthe name or address the end user connects to
subnetthe internal range of the server; every user address comes from it
next_ipfirst free address, used when you omit ip
writablefalse means the service is not active and no account is issued
agentauto-apply status on the router, see the next section
This number is not your customer code (such as SN-571100). The customer code identifies the account, not the service.

3Auto-apply on the router

Your router is not reachable from our side, so we never connect to it: it asks us. Running the two lines below installs a script and a scheduler that read your queue regularly and apply new peers. Without this, everything still works, you just run the router command yourself.

  1. Fetch the agent token with the call below; the response also returns the two ready to run lines.
  2. Run those two lines in your MikroTik router terminal.
  3. The first run takes a few seconds because the root certificate is installed too.
  4. After that the status should turn alive.
POST https://servernet.cloud/api/v1/tunnel/49/agent

{"ok": true, "data": {
  "token": "sna_49_xxxxxxxx",
  "replaced": false,
  "install": [
    "/tool fetch url=\"https://servernet.cloud/agent/tunnel/install\" http-header-field=\"X-Agent-Token: sna_49_xxxxxxxx\" dst-path=snet-agent.rsc",
    "/import file-name=snet-agent.rsc"
  ]
}}
Each call to this endpoint revokes the previous token immediately, and auto-apply stays down until the two new lines run on the router. Open the router terminal first, then make the call.

installed means a token was issued; alive means the router actually checked in recently. The two are deliberately separate: an agent that is installed but silent does not work from your point of view, and a single label would hide that.

GET https://servernet.cloud/api/v1/tunnel/49/agent

{"ok": true, "data": {
  "installed": true, "alive": true,
  "last_seen_at": "2026-08-24T08:16:18+00:00",
  "pending_jobs": 0
}}

The server never sends the router a command. The reply carries only three values, name, address and public key; the script validates each one separately and builds the command itself. The address is only accepted from inside your own range.

4Create a user

POST https://servernet.cloud/api/v1/tunnel/49/accounts
{"name": "ali-mobile"}

{"ok": true, "data": {
  "name": "ali-mobile", "ip": "10.77.0.5",
  "public_key": "...", "private_key": "...",
  "delivery": {"mode": "agent", "status": "pending",
               "job_id": 41, "agent_alive": true},
  "router_command": "/interface/wireguard/peers/add ...",
  "config": { ... sing-box ... }
}}

Input fields

namerequired, 2 to 24 lowercase latin letters, digits, hyphen or underscore
ipoptional, omit it and the next free address is chosen
formatoptional, singbox is the default; legacy is for older apps
The private key is returned in this one response and is never stored. If you do not keep it there is no way to recover it; you delete the user and create it again.

The config field is a complete sing-box configuration; hand it to the end user to save with a json extension and import into their app. You do not have to build anything.

The delivery field says where the work is: mode agent means it is queued for the router and will be created within seconds; mode manual means no agent is installed and you run router_command yourself. router_command is returned in both cases, because the agent may be down and that line is then the only way out.

A 201 alone means recorded, not connected. Until the account state reaches active, that configuration does not work.

5List users and their state

Every user carries a state field that reports what the end user experiences, not an internal label.

GET https://servernet.cloud/api/v1/tunnel/49/accounts

{"ok": true, "data": {
  "service_id": 49, "next_ip": "10.77.0.6",
  "agent": {"installed": true, "alive": true, "pending_jobs": 0},
  "accounts": [
    {"name": "ali-mobile", "ip": "10.77.0.5",
     "public_key": "...", "issued_at": "...",
     "state": "active"}
  ]
}}
active applied on the router, the user connects pending still queued for the router, wait a few seconds failed the router refused it or the agent never ran; check the router log

Users created before this queue existed have no job row and are deliberately reported as active rather than unknown: they are all sitting correctly on the router.

6Delete a user

DELETE https://servernet.cloud/api/v1/tunnel/49/accounts/ali-mobile

{"ok": true, "data": {
  "name": "ali-mobile",
  "delivery": {"mode": "agent", "status": "pending"},
  "router_command": "/interface/wireguard/peers/remove [find name=\"ali-mobile\"]"
}}
With the agent enabled the peer is removed from the router for you. Without it, that user keeps connecting until you run the removal router_command: deleting from the listing alone does not cut access.

7Errors and limits

A failed response always carries a machine readable code in the error field. Do not depend on the message text, it may change.

insufficient_scopethe token lacks the required scope
not_foundno such server or account in your account
service_not_activethe service is suspended, expired or cancelled
bad_namethe name does not match the allowed pattern
name_takenan account with this name already exists
bad_ipthe address is not from this server internal range
ip_takenthat address is already in use
limit_reachedyou reached the account limit

Rate limits

read120 / 1 min
create and delete20 / 1 min
agent token issue5 / 1 min
maximum users per server100

← Domain Reseller API Reference