Teitunnel

Server API

Automate a Teitunnel server with its HTTP API.

teitunnel-cli serve runs a server's tunnels and offers a small HTTP API next to its web dashboard (see Servers and containers). The machine-readable description is at /api/openapi.json.

Authentication

Create a key on the server and send it as a bearer token:

teitunnel-cli api-key create deploy     # printed once; store it as a secret
teitunnel-cli api-key list
teitunnel-cli api-key revoke 3
Authorization: Bearer ttk_…

Keys are stored only as hashes. The browser dashboard signs in with a password instead.

Endpoints

MethodPathDoes
GET/api/overviewAccounts, this machine's tunnels and routes with their status, shares on your domains
POST/api/previewPlans a change. Nothing changes.
POST/api/applyApplies exactly the plan you previewed

Making a change

A change is two calls. The preview returns the plan and its fingerprint; applying sends the same change with that fingerprint. If anything changed in Cloudflare in between, apply answers 409 and nothing is changed: preview again.

curl -s -X POST http://127.0.0.1:8765/api/preview \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"accountId":"…","change":{"type":"addRoute","route":{"hostname":"app.example.com","origin":"3000","path":null,"access":null}}}'

The response lists the steps (in English) and warnings; requiresConfirmation is true when the change would replace DNS records Teitunnel didn't create. To apply:

curl -s -X POST http://127.0.0.1:8765/api/apply \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"accountId":"…","change":{…the same change…},"fingerprint":"…","confirmed":false}'

The outcome is applied, or rolledBack (nothing is left changed) or partiallyApplied (with what couldn't be undone).

Changes

typeFields
addRouteroute: { hostname, origin, path, access }
updateRoutehostname, path, route
removeRoutehostname, path
createTunnelname
removeTunnel(with tunnelId to choose which)
balanceRoute, unbalanceRoutehostname
addNetwork, removeNetworknetwork

tunnelId (next to accountId) picks one of the machine's tunnels; without it, the default tunnel. access is { "emails": [...], "emailDomains": [...] } or null.

Edit on GitHub

On this page