- All API endpoints require an API key.
- Provide it via HTTP header:
X-API-Key: <your-key>or as query parameter?api_key=<your-key>. - Set the key with environment variable
WG_API_KEY(default:change-this-api-key).
Replace
http://localhost:8000with your host and setX-API-Keyheader on all requests.
- GET /api/tunnels
- Auth: required
- Response: 200 OK — JSON array
cURL example:
curl -H "X-API-Key: mykey" \
http://localhost:8000/api/tunnelsThese endpoints provide current WG transfer counters, a short-term live throughput estimate (bytes/sec) and cumulative transferred bytes tracked by the service.
- GET /api/tunnels/{id}/stats
- Query param:
sample_ms(optional) short sampling window in ms for live estimation (default 500) - Returns JSON with:
current_rx,current_tx(bytes),live_rx_bps,live_tx_bps(bytes/sec),cumulative_rx,cumulative_tx,last_seen.
- Query param:
cURL example (get stats, 1s sampling):
curl -H "X-API-Key: mykey" \
"http://localhost:8000/api/tunnels/1/stats?sample_ms=1000"Sample response:
{
"interface": "my-tunnel",
"peer_pub": "...",
"current_rx": 123456,
"current_tx": 654321,
"live_rx_bps": 1024.5,
"live_tx_bps": 512.3,
"cumulative_rx": 9876543,
"cumulative_tx": 3456789,
"last_seen": "2026-01-17T12:34:56.789"
}- POST /api/tunnels/{id}/stats/reset
- Resets the stored cumulative counters to zero and sets
last_countersto the currentwgcounter snapshot.
- Resets the stored cumulative counters to zero and sets
cURL example (reset counters):
curl -X POST -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1/stats/resetSample response:
{ "reset": true, "last_counters": { "rx": 123456, "tx": 654321 } }- GET /api/tunnels/{id}
- Auth: required
- Response: 200 OK or 404 if not found
- Body: JSON object (same fields as list single tunnel)
Sample response (200):
[
{
"id": 1,
"name": "my-tunnel",
"created": "2026-01-17T12:00:00",
"external_subnet": "185.14.93.80/28",
"allocated_subnet": "10.200.200.0/30",
"server_ip": "10.200.200.1",
"listen_port": 51820,
"server_pub": "...",
"peer_pub": "...",
"peer_ip": "10.200.200.2",
"endpoint": "185.14.93.8:51880",
"wg_started": true,
"wg_start_msg": "started"
}
]- GET /api/tunnels/{id}
- Auth: required
cURL example:
curl -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1Sample response (200): same object as list entry.
- POST /api/tunnels
- Auth: required
- Content-Type: application/json
- Body fields:
name(optional)external_subnet(required, CIDR) e.g.185.14.93.80/28endpoint(optional) e.g.185.14.93.8:51880
cURL example:
curl -X POST -H "X-API-Key: mykey" -H "Content-Type: application/json" \
-d '{"name":"my-tunnel","external_subnet":"185.14.93.80/28","endpoint":"185.14.93.8:51880"}' \
http://localhost:8000/api/tunnelsSample response (201): JSON of created tunnel (non-sensitive fields), plus wg_started and wg_start_msg.
- GET /api/tunnels/{id}/download/server
- Auth: required
- Returns: Attachment (WireGuard server config)
cURL example (save to file):
curl -H "X-API-Key: mykey" \
http://localhost:8000/api/tunnels/1/download/server -o my-tunnel-server.conf- GET /api/tunnels/{id}/download/client
- Auth: required
cURL example (save):
curl -H "X-API-Key: mykey" \
http://localhost:8000/api/tunnels/1/download/client -o my-tunnel-client.conf- GET /api/tunnels/{id}/config
- Auth: required
- Returns: text/plain body with server config
cURL example:
curl -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1/config- PUT /api/tunnels/{id}
- Auth: required
- Content-Type: application/json
Two main modes:
- Replace raw config text
Body example (replace config and restart):
{
"config": "[Interface]\nAddress = 10.200.200.1/32\nPrivateKey = <...>\nListenPort = 51820\n...",
"restart": true
}cURL example:
curl -X PUT -H "X-API-Key: mykey" -H "Content-Type: application/json" \
-d @config_payload.json \
http://localhost:8000/api/tunnels/1- Edit metadata (regenerate config)
Body example (change name and endpoint, don't restart):
{
"name": "renamed-tunnel",
"endpoint": "185.14.93.9:51880",
"restart": false
}Response (200): updated public tunnel info JSON.
- POST /api/tunnels/{id}/start
- Auth: required
- Response:
{ "wg_started": true|false, "msg": "..." }
cURL example:
curl -X POST -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1/start- POST /api/tunnels/{id}/stop
- Auth: required
- Response:
{ "stopped": true|false, "msg": "..." }
cURL example:
curl -X POST -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1/stop- DELETE /api/tunnels/{id}
- Auth: required
- Behavior: stops interface (best-effort), deletes config file, removes tunnel from records
- Response:
200 OK { "deleted": true }
cURL example:
curl -X DELETE -H "X-API-Key: mykey" http://localhost:8000/api/tunnels/1- 401 Unauthorized — bad or missing API key
- 400 Bad Request — invalid input (invalid JSON, invalid CIDR, missing required field)
- 404 Not Found — tunnel id or config not found
Sample error response (JSON):
{ "error": "invalid external_subnet" }- The API never returns private keys. Private keys are stored for generating configs but not exposed via API responses.
- The service chooses the next available /30 inside
10.200.200.0/24, skipping subnets with IPs already present on the system or allocated to other tunnels. - Listen ports are assigned starting at
51820and increment by 1; the service also considers any running WireGuard listeners discovered viawg. - When using
wg-quick upvia the API, the process running the app needs sufficient privileges (usually root). Use with care.
If you want, I can add Postman examples or a small shell script showing automated tunnel creation and download. Let me know which format you prefer.