Skip to content

The Tailrelay Web UI serves an HTTP/JSON API on port 8021 for managing Tailscale connectivity, tailscale serve / tailscale funnel relays, configuration backups, and container logs.

The reference below is rendered directly from docs/openapi.yaml, the project's OpenAPI specification.

Tailrelay Web UI API 0.9.3

HTTP/JSON API served by the Tailrelay Go Web UI on port 8021. It manages Tailscale connectivity and tailscale serve / tailscale funnel relays, plus configuration backups and container logs.

This document is generated from the actual route registration in webui/internal/web/server.go and the handler implementations under webui/internal/handlers/. Where the code and the README diverge, this spec follows the code.

Authentication

Every /api/* route except the public ones (GET /api/info, GET /api/auth/status, POST /api/auth/setup, POST /api/auth/login) requires authentication. RequireAuth accepts either:

  • a Bearer token — Authorization: Bearer <token> matching the static token written to /var/lib/tailscale/.webui_token on first start; or
  • a session cookie — tailrelay_session (HttpOnly, SameSite=Strict, Secure when served over TLS, 24h expiry). The cookie is set by POST /api/auth/setup, POST /api/auth/login, and by GET /api/tailscale/poll once the node is connected.

On failure, /api/* paths return 401 with a JSON body {"error":"unauthorized"}; non-API paths receive a 303 redirect to /login.

Error response shapes

Error bodies are not uniform:

  • Serve handlers use JSON {"status":"success"|"pending"|"error","message":...} for success (200), tailscale-not-ready (202), and funnel-not-allowed (409).
  • Most other errors are emitted via Go's http.Error, producing a plain-text body (text/plain) with the status code — not JSON.
  • The auth middleware 401 is JSON {"error":"unauthorized"}.

Individual operations document their concrete responses below.

Legacy endpoints (removed)

/api/caddy/* and /api/socat/* are retained only as 410 Gone shims and are intentionally omitted from this spec.


Contact: Tailrelay
License: BSD-3-Clause

Servers

Description URL
Local access http://localhost:8021
Tailscale MagicDNS access (once connected and HTTPS is enabled) https://{hostname}.{tailnet}.ts.net:8021

Info


GET /api/info

Build metadata

Description

Returns version and commit of the running binary. Public.

Responses

{
    "version": "string",
    "commit": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "version": {
            "type": "string"
        },
        "commit": {
            "type": "string"
        }
    },
    "required": [
        "version",
        "commit"
    ]
}

Auth


GET /api/auth/status

Setup / authentication status

Description

Whether first-run setup is needed and whether the caller has a valid session. Public.

Responses

{
    "needsSetup": true,
    "authenticated": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "needsSetup": {
            "type": "boolean"
        },
        "authenticated": {
            "type": "boolean"
        }
    },
    "required": [
        "needsSetup",
        "authenticated"
    ]
}

POST /api/auth/setup

First-run admin password setup

Description

Creates the initial admin password (bcrypt) and sets a session cookie. Fails if setup was already completed. Public.

Request body

{
    "password": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "password": {
            "type": "string"
        }
    },
    "required": [
        "password"
    ]
}

Responses

{
    "success": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "success": {
            "type": "boolean"
        }
    },
    "required": [
        "success"
    ]
}

Response headers

Name Description Schema
Set-Cookie tailrelay_session=...; HttpOnly; SameSite=Strict string

Refer to the common response description: PlainTextError.

Refer to the common response description: PlainTextError.


POST /api/auth/login

Password login

Description

Validates the admin password and sets a session cookie. Public.

Request body

{
    "password": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "password": {
            "type": "string"
        }
    },
    "required": [
        "password"
    ]
}

Responses

{
    "success": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "success": {
            "type": "boolean"
        }
    },
    "required": [
        "success"
    ]
}

Response headers

Name Description Schema
Set-Cookie string

Refer to the common response description: PlainTextError.

Refer to the common response description: PlainTextError.


POST /api/auth/logout

Log out

Description

Clears the session cookie.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "success": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "success": {
            "type": "boolean"
        }
    },
    "required": [
        "success"
    ]
}

Refer to the common response description: Unauthorized.


POST /api/auth/change-password

Change admin password

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "currentPassword": "string",
    "newPassword": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "currentPassword": {
            "type": "string"
        },
        "newPassword": {
            "type": "string"
        }
    },
    "required": [
        "currentPassword",
        "newPassword"
    ]
}

Responses

{
    "success": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "success": {
            "type": "boolean"
        }
    },
    "required": [
        "success"
    ]
}

Refer to the common response description: PlainTextError.

Refer to the common response description: PlainTextError.

Status


GET /api/status

Aggregate system status

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "timestamp": "2022-04-13T15:42:05.901Z",
    "version": "string",
    "services": {
        "webui": "running",
        "tailscale": {
            "connected": true,
            "state": "Running"
        },
        "serve": {
            "https_relays": 0,
            "tcp_relays": 0
        }
    }
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "Aggregate status from DashboardHandler.APIStatus.",
    "properties": {
        "timestamp": {
            "type": "string",
            "format": "date-time"
        },
        "version": {
            "type": "string",
            "description": "Hardcoded server-reported version string."
        },
        "services": {
            "type": "object",
            "properties": {
                "webui": {
                    "type": "string",
                    "examples": [
                        "running"
                    ]
                },
                "tailscale": {
                    "type": "object",
                    "properties": {
                        "connected": {
                            "type": "boolean"
                        },
                        "state": {
                            "type": "string",
                            "examples": [
                                "Running"
                            ]
                        }
                    }
                },
                "serve": {
                    "type": "object",
                    "properties": {
                        "https_relays": {
                            "type": "integer"
                        },
                        "tcp_relays": {
                            "type": "integer"
                        }
                    }
                }
            }
        }
    }
}

Refer to the common response description: Unauthorized.


GET /api/targets

List configured targets

Description

Returns the raw contents of targets.json. Returns [] if the file is missing or unparseable.

Entries are opaque to the backend, but the Web UI understands these optional fields when offering a preset target in the Add Relay modal: target_name, app_id, host, port, type (relay/tcp/ proxy/https), protocol (http/https/tcp), and icon_url (an http/https URL or small data:image/* URI) which seeds the icon field when the preset is selected.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    {}
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "type": "object",
        "additionalProperties": true
    }
}

Refer to the common response description: Unauthorized.

Tailscale


POST /api/tailscale/login

Begin interactive Tailscale login

Description

Starts an interactive login and returns the auth URL to visit. If a control server is persisted via POST /api/tailscale/control-server/update, it's automatically passed as tailscale login --login-server=<url>.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "status": "success",
    "auth_url": "string",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "auth_url": {
            "type": "string",
            "format": "uri"
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


POST /api/tailscale/login-with-key

Non-interactive login with a pre-shared auth key

Description

Authenticates using a tskey- (Tailscale) or hskey- (Headscale) auth key, then asynchronously reconciles relays. If a control server is persisted via POST /api/tailscale/control-server/update, it's automatically passed as tailscale up --login-server=<url>.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "auth_key": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "auth_key": {
            "type": "string",
            "description": "Must be non-empty and start with `tskey-` (Tailscale) or `hskey-` (Headscale)."
        }
    },
    "required": [
        "auth_key"
    ]
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: TailscaleError.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


POST /api/tailscale/logout

Deauthorize (log out) the node

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


POST /api/tailscale/connect

Bring Tailscale up

Description

Runs tailscale up, then asynchronously reconciles relays.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


POST /api/tailscale/disconnect

Bring Tailscale down

Description

Runs tailscale down.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


POST /api/tailscale/hostname

Change the Tailscale hostname

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "hostname": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "hostname": {
            "type": "string"
        }
    },
    "required": [
        "hostname"
    ]
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: TailscaleError.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


GET /api/tailscale/control-server

Get the persisted control server

Description

Returns the custom control server URL (e.g. a self-hosted Headscale instance) persisted via POST /api/tailscale/control-server/update. An empty value means Tailscale's default control plane.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "control_server": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "control_server": {
            "type": "string",
            "description": "Persisted custom control server URL (e.g. a self-hosted Headscale\ninstance). Empty means Tailscale's default control plane.\n"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.


POST /api/tailscale/control-server/update

Set the persisted control server

Description

Validates and persists a custom control server URL to webui.yaml. It's automatically applied to subsequent POST /api/tailscale/login and POST /api/tailscale/login-with-key calls. Has no effect on a device that's already registered until it's logged out and re-authenticated. Pass an empty string to reset to Tailscale's default control plane.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "control_server": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "control_server": {
            "type": "string",
            "description": "Must be a valid `http://` or `https://` URL, or empty to reset to\nTailscale's default control plane.\n"
        }
    }
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: TailscaleError.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: TailscaleError.


GET /api/tailscale/status

Tailscale status summary

Description

Returns a StatusSummary. On daemon error the handler still responds 200 with a degraded object (Connected:false, BackendState:"Unknown"). Note: the Go struct has no JSON tags, so field names are capitalized.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "Connected": true,
    "BackendState": "Running",
    "Hostname": "string",
    "MagicDNSName": "string",
    "IPv4": "string",
    "IPv6": "string",
    "TailnetName": "string",
    "Version": "string",
    "PeerCount": 0,
    "ActivePeers": 0,
    "Health": [
        "string"
    ],
    "LastCheck": "2022-04-13T15:42:05.901Z"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "The Go struct has no JSON tags, so keys are the capitalized Go field\nnames.\n",
    "properties": {
        "Connected": {
            "type": "boolean"
        },
        "BackendState": {
            "type": "string",
            "examples": [
                "Running"
            ]
        },
        "Hostname": {
            "type": "string"
        },
        "MagicDNSName": {
            "type": "string"
        },
        "IPv4": {
            "type": "string"
        },
        "IPv6": {
            "type": "string"
        },
        "TailnetName": {
            "type": "string"
        },
        "Version": {
            "type": "string"
        },
        "PeerCount": {
            "type": "integer"
        },
        "ActivePeers": {
            "type": "integer"
        },
        "Health": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "LastCheck": {
            "type": "string",
            "format": "date-time"
        }
    }
}

Refer to the common response description: Unauthorized.


GET /api/tailscale/peers

List Tailscale peers

Description

Note: the PeerInfo struct has no JSON tags, so field names are capitalized.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    {
        "Hostname": "string",
        "DNSName": "string",
        "OS": "string",
        "IPv4": "string",
        "IPv6": "string",
        "TailscaleIPs": [
            "string"
        ],
        "UserEmail": "string",
        "Active": true,
        "Online": true,
        "LastSeen": "2022-04-13T15:42:05.901Z",
        "Relay": "string",
        "ExitNode": true
    }
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/PeerInfo"
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: TailscaleError.


GET /api/tailscale/poll

Poll connection state

Description

Reports whether the node is connected. When connected, sets the tailrelay_session cookie.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "connected": true,
    "timestamp": 110
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "connected": {
            "type": "boolean"
        },
        "timestamp": {
            "type": "integer",
            "format": "int64",
            "description": "Unix timestamp (seconds)."
        }
    },
    "required": [
        "connected",
        "timestamp"
    ]
}

Response headers

Name Description Schema
Set-Cookie Set only when connected string

Refer to the common response description: Unauthorized.

Refer to the common response description: TailscaleError.


GET /api/tailscale/networking

Networking preferences summary

Description

Returns the current exit-node, subnet route, and SSH preferences via tailscale set's underlying prefs. AdvertiseExitNode and the exit-node CIDRs (0.0.0.0/0, ::/0) are derived from the same AdvertiseRoutes preference used for subnet routes; the AdvertiseRoutes field returned here excludes those two, exposing only custom subnet routes.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "AdvertiseExitNode": true,
    "ExitNodeAllowLANAccess": true,
    "AdvertiseRoutes": [
        "string"
    ],
    "AcceptRoutes": true,
    "ExitNode": "string",
    "SSH": true,
    "UserspaceNetworking": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "The Go struct has no JSON tags, so keys are capitalized Go field names.",
    "properties": {
        "AdvertiseExitNode": {
            "type": "boolean"
        },
        "ExitNodeAllowLANAccess": {
            "type": "boolean"
        },
        "AdvertiseRoutes": {
            "type": "array",
            "items": {
                "type": "string"
            },
            "description": "Custom subnet routes only; the exit-node CIDRs (0.0.0.0/0, ::/0) are excluded."
        },
        "AcceptRoutes": {
            "type": "boolean"
        },
        "ExitNode": {
            "type": "string",
            "description": "IP of the currently-selected exit node, or empty if none is in use."
        },
        "SSH": {
            "type": "boolean"
        },
        "UserspaceNetworking": {
            "type": "boolean",
            "description": "True when tailscaled is running with --tun=userspace-networking (Tailrelay's only mode). When true, routing this node's own traffic through a peer exit node is impossible — the UI only offers \"Run as exit node\" and \"None\", and the update endpoint rejects a non-empty `exit_node` with 409. Advertised exit-node usage from other peers still works.\n"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: TailscaleError.


POST /api/tailscale/networking/update

Update networking preferences

Description

Applies a partial update via a single tailscale set invocation. Only fields present in the request body are changed; omitted fields are left untouched. advertise_routes entries must be valid CIDRs with no host bits set; 0.0.0.0/0 and ::/0 are rejected since exit-node advertisement is controlled via advertise_exit_node instead.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "advertise_exit_node": true,
    "exit_node_allow_lan_access": true,
    "advertise_routes": [
        "string"
    ],
    "accept_routes": true,
    "exit_node": "string",
    "ssh": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "All fields are optional; only fields present in the request are changed.",
    "properties": {
        "advertise_exit_node": {
            "type": "boolean"
        },
        "exit_node_allow_lan_access": {
            "type": "boolean"
        },
        "advertise_routes": {
            "type": "array",
            "items": {
                "type": "string"
            },
            "description": "Replaces the full set of advertised subnet routes. Each entry must be a CIDR with no host bits set (e.g. `192.168.1.0/24`). `0.0.0.0/0` and `::/0` are rejected; use `advertise_exit_node` instead. Pass `[]` to clear all advertised routes.\n"
        },
        "accept_routes": {
            "type": "boolean"
        },
        "exit_node": {
            "type": "string",
            "description": "IP or MagicDNS name of the peer to use as an exit node, or `\"\"` to clear. Under userspace-networking mode a non-empty value is rejected with 409 since the node cannot install host routes to redirect its own traffic through a peer.\n"
        },
        "ssh": {
            "type": "boolean"
        }
    }
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    }
}

Refer to the common response description: TailscaleError.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Schema of the response body
{
    "description": "Tailscale operation error",
    "content": {
        "application/json": {
            "schema": {
                "$ref": "#/components/schemas/TailscaleError"
            }
        }
    }
}

Refer to the common response description: TailscaleError.

Serve HTTPS


GET /api/serve/https/list

List web relays

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    null
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/ServeRelayWithRunning"
    }
}

Refer to the common response description: Unauthorized.


GET /api/serve/https/get

Get one web relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Refer to the common response description: Unauthorized.


POST /api/serve/https/create

Create a web relay

Description

Accepts either a JSON ServeRelay body or multipart/form-data. Type is forced to https; ID defaults to https-<listen_port>. The listener uses HTTPS with Tailscale's control plane and HTTP with a custom control server.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

{
    "id": "string",
    "port": "string",
    "target": "string",
    "tls": "string",
    "enabled": "string",
    "autostart": "string",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "Multipart form accepted by HTTPS create/update.",
    "properties": {
        "id": {
            "type": "string"
        },
        "port": {
            "type": "string",
            "description": "Listen port."
        },
        "target": {
            "type": "string",
            "description": "Target as `host:port`."
        },
        "tls": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "enabled": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "autostart": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon URL or small `data:image/*` URI (see `ServeRelay.icon_url`).\nPersisted onto the relay and rendered on its card.\n"
        }
    }
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


PUT /api/serve/https/update

Update an HTTPS relay

Description

JSON or multipart; id is required.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

{
    "id": "string",
    "port": "string",
    "target": "string",
    "tls": "string",
    "enabled": "string",
    "autostart": "string",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "Multipart form accepted by HTTPS create/update.",
    "properties": {
        "id": {
            "type": "string"
        },
        "port": {
            "type": "string",
            "description": "Listen port."
        },
        "target": {
            "type": "string",
            "description": "Target as `host:port`."
        },
        "tls": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "enabled": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "autostart": {
            "type": "string",
            "description": "Bool-ish (true/1/on/yes)."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon URL or small `data:image/*` URI (see `ServeRelay.icon_url`).\nPersisted onto the relay and rendered on its card.\n"
        }
    }
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: PlainTextError.


DELETE /api/serve/https/delete

Delete an HTTPS relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.


POST /api/serve/https/toggle

Enable/disable an HTTPS relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "enabled": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "enabled": {
            "type": "boolean"
        }
    },
    "required": [
        "id",
        "enabled"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.

Serve TCP


GET /api/serve/tcp/list

List TCP relays

Description

Unlike HTTPS/funnel lists, each item wraps the relay: {relay, running}.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    {
        "relay": {
            "id": "string",
            "type": "https",
            "listen_port": 0,
            "target_host": "string",
            "target_port": 0,
            "target_https": true,
            "enabled": true,
            "autostart": true,
            "funnel_transport": "https",
            "icon_url": "string"
        },
        "running": true
    }
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/TCPRelayStatus"
    }
}

Refer to the common response description: Unauthorized.


GET /api/serve/tcp/get

Get one TCP relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Refer to the common response description: Unauthorized.


POST /api/serve/tcp/create

Create a TCP relay

Description

JSON ServeRelay. Requires listen_port, target_host, target_port. Type forced to tcp; ID defaults to tcp-<listen_port>.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: PlainTextError.


PUT /api/serve/tcp/update

Update a TCP relay

Description

JSON ServeRelay; id required.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: PlainTextError.


DELETE /api/serve/tcp/delete

Delete a TCP relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.


POST /api/serve/tcp/toggle

Enable/disable a TCP relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "enabled": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "enabled": {
            "type": "boolean"
        }
    },
    "required": [
        "id",
        "enabled"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.

Serve


POST /api/serve/reload

Reconcile all enabled relays

Description

Reconciles the live tailscale serve configuration against all enabled relays. This is the only reconcile endpoint; the per-type /reconcile, /start, /stop, /restart endpoints described in older docs do not exist.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: FunnelNotAllowed.

Refer to the common response description: PlainTextError.

Serve Funnel


GET /api/serve/funnel/list

List funnel relays

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    null
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/ServeRelayWithRunning"
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


GET /api/serve/funnel/get

Get one funnel relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Refer to the common response description: Unauthorized.


POST /api/serve/funnel/create

Create a funnel relay

Description

JSON ServeRelay. listen_port must be a funnel-eligible port (443, 8443, or 10000). funnel_transport selects https or tcp. Type forced to funnel; ID defaults to funnel-<listen_port>. Requires the tailnet funnel node attribute, else 409.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: FunnelNotAllowed.

Refer to the common response description: PlainTextError.


PUT /api/serve/funnel/update

Update a funnel relay

Description

JSON ServeRelay; id required.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "type": "https",
    "listen_port": 0,
    "target_host": "string",
    "target_port": 0,
    "target_https": true,
    "enabled": true,
    "autostart": true,
    "funnel_transport": "https",
    "icon_url": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "description": "A relay backed by `tailscale serve` or `tailscale funnel`.",
    "properties": {
        "id": {
            "type": "string"
        },
        "type": {
            "type": "string",
            "enum": [
                "https",
                "tcp",
                "funnel"
            ]
        },
        "listen_port": {
            "type": "integer"
        },
        "target_host": {
            "type": "string"
        },
        "target_port": {
            "type": "integer"
        },
        "target_https": {
            "type": "boolean"
        },
        "enabled": {
            "type": "boolean"
        },
        "autostart": {
            "type": "boolean"
        },
        "funnel_transport": {
            "type": "string",
            "enum": [
                "https",
                "tcp"
            ],
            "description": "Only meaningful when `type` is `funnel`."
        },
        "icon_url": {
            "type": "string",
            "description": "Optional icon rendered on the relay card instead of the default\ntype glyph. Accepts an `http`/`https` URL or a small\n`data:image/*` URI (capped at 256 KiB to avoid bloating\n`serve_relays.json`). Empty/omitted means \"use the default glyph\".\nOther schemes (e.g. `javascript:`) are rejected with 400.\n"
        }
    },
    "required": [
        "id",
        "type",
        "listen_port",
        "target_host",
        "target_port",
        "target_https",
        "enabled",
        "autostart"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: ServePending.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeMethodError.

Refer to the common response description: FunnelNotAllowed.

Refer to the common response description: PlainTextError.


DELETE /api/serve/funnel/delete

Delete a funnel relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
id query string No Relay identifier

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.


POST /api/serve/funnel/toggle

Enable/disable a funnel relay

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "id": "string",
    "enabled": true
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "enabled": {
            "type": "boolean"
        }
    },
    "required": [
        "id",
        "enabled"
    ]
}

Responses

Refer to the common response description: ServeSuccess.

Refer to the common response description: Unauthorized.

Refer to the common response description: ServeNotFound.

Refer to the common response description: ServeMethodError.

Refer to the common response description: FunnelNotAllowed.

Backup


GET /api/backup/list

List backups

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

[
    {
        "filename": "string",
        "size": 6,
        "timestamp": "2022-04-13T15:42:05.901Z",
        "metadata": {
            "timestamp": "2022-04-13T15:42:05.901Z",
            "version": "string",
            "hostname": "string",
            "backup_type": "full"
        }
    }
]
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "array",
    "items": {
        "$ref": "#/components/schemas/BackupInfo"
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


POST /api/backup/create

Create a backup

Description

backup_type defaults to full if absent/empty.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "backup_type": "full"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "backup_type": {
            "type": "string",
            "enum": [
                "full",
                "config-only"
            ],
            "default": "full"
        }
    }
}

Responses

{
    "status": "success",
    "message": "string",
    "backup_path": "string",
    "filename": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        },
        "backup_path": {
            "type": "string"
        },
        "filename": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


POST /api/backup/restore

Restore from a backup

Description

Restores the named backup, then reconciles serve.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "filename": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "filename": {
            "type": "string"
        }
    },
    "required": [
        "filename"
    ]
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


DELETE /api/backup/delete

Delete a backup

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
filename query string No Backup filename (`*.tar.gz`)

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


POST /api/backup/rename

Rename a backup

Description

.tar.gz is appended to the new name if missing.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "old_filename": "string",
    "new_filename": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "old_filename": {
            "type": "string"
        },
        "new_filename": {
            "type": "string"
        }
    },
    "required": [
        "old_filename",
        "new_filename"
    ]
}

Responses

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


GET /api/backup/download

Download a backup

Description

Path-traversal-guarded. Streams the .tar.gz archive.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.
filename query string No Backup filename (`*.tar.gz`)

Responses

"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ="
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "string",
    "format": "binary"
}

Response headers

Name Description Schema
Content-Disposition attachment; filename=... string

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.


POST /api/backup/upload

Upload a backup

Description

multipart/form-data, file field backup, max 32 MB. Filename must end in .tar.gz.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "backup": "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ="
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "backup": {
            "type": "string",
            "format": "binary"
        }
    },
    "required": [
        "backup"
    ]
}

Responses

{
    "status": "success",
    "message": "string",
    "filename": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        },
        "filename": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.

Refer to the common response description: PlainTextError.

Logs


GET /api/logs

Historical logs and current level

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "logs": [
        {
            "timestamp": "2022-04-13T15:42:05.901Z",
            "level": "string",
            "message": "string",
            "source": "string"
        }
    ],
    "level": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "logs": {
            "type": "array",
            "items": {
                "$ref": "#/components/schemas/LogEntry"
            }
        },
        "level": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.


GET /api/logs/stream

Live log stream (SSE)

Description

Server-Sent Events stream (text/event-stream). Emits data: {"connected": true} first, then one data: <LogEntry JSON> per log event.

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

"string"
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "string"
}

Refer to the common response description: Unauthorized.


GET /api/logs/level

Get current log level

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Responses

{
    "level": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "level": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.


POST /api/logs/level

Set log level

Input parameters

Parameter In Type Default Nullable Description
sessionCookie cookie string N/A No Session cookie set after setup/login or successful Tailscale poll.
bearerToken header string N/A No Static token from `/var/lib/tailscale/.webui_token`.

Request body

{
    "level": "debug"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the request body
{
    "type": "object",
    "properties": {
        "level": {
            "type": "string",
            "enum": [
                "debug",
                "info",
                "warn",
                "error"
            ]
        }
    },
    "required": [
        "level"
    ]
}

Responses

{
    "success": true,
    "level": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "success": {
            "type": "boolean"
        },
        "level": {
            "type": "string"
        }
    }
}

Refer to the common response description: Unauthorized.


Schemas

AuthError

Name Type Description
error string

AuthKeyRequest

Name Type Description
auth_key string Must be non-empty and start with `tskey-` (Tailscale) or `hskey-` (Headscale).

AuthStatus

Name Type Description
authenticated boolean
needsSetup boolean

BackupCreateRequest

Name Type Description
backup_type string

BackupCreateResult

Name Type Description
backup_path string
filename string
message string
status string

BackupFilenameRequest

Name Type Description
filename string

BackupInfo

Name Type Description
filename string
metadata BackupMetadata
size integer(int64)
timestamp string(date-time)

BackupMetadata

Name Type Description
backup_type string
hostname string
timestamp string(date-time)
version string

BackupRenameRequest

Name Type Description
new_filename string
old_filename string

BackupUploadForm

Name Type Description
backup string(binary)

BackupUploadResult

Name Type Description
filename string
message string
status string

ChangePasswordRequest

Name Type Description
currentPassword string
newPassword string

ControlServerRequest

Name Type Description
control_server string Must be a valid `http://` or `https://` URL, or empty to reset to Tailscale's default control plane.

ControlServerSummary

Name Type Description
control_server string Persisted custom control server URL (e.g. a self-hosted Headscale instance). Empty means Tailscale's default control plane.

HostnameRequest

Name Type Description
hostname string

HTTPSRelayForm

Name Type Description
autostart string Bool-ish (true/1/on/yes).
enabled string Bool-ish (true/1/on/yes).
icon_url string Optional icon URL or small `data:image/*` URI (see `ServeRelay.icon_url`). Persisted onto the relay and rendered on its card.
id string
port string Listen port.
target string Target as `host:port`.
tls string Bool-ish (true/1/on/yes).

InfoResponse

Name Type Description
commit string
version string

LogEntry

Name Type Description
level string
message string
source string
timestamp string(date-time)

LogLevelRequest

Name Type Description
level string

LogLevelResponse

Name Type Description
level string

LogLevelSetResult

Name Type Description
level string
success boolean

LogsResponse

Name Type Description
level string
logs Array<LogEntry>

NetworkingSummary

Name Type Description
AcceptRoutes boolean
AdvertiseExitNode boolean
AdvertiseRoutes Array<string> Custom subnet routes only; the exit-node CIDRs (0.0.0.0/0, ::/0) are excluded.
ExitNode string IP of the currently-selected exit node, or empty if none is in use.
ExitNodeAllowLANAccess boolean
SSH boolean
UserspaceNetworking boolean True when tailscaled is running with --tun=userspace-networking (Tailrelay's only mode). When true, routing this node's own traffic through a peer exit node is impossible — the UI only offers "Run as exit node" and "None", and the update endpoint rejects a non-empty `exit_node` with 409. Advertised exit-node usage from other peers still works.

NetworkingUpdateRequest

Name Type Description
accept_routes boolean
advertise_exit_node boolean
advertise_routes Array<string> Replaces the full set of advertised subnet routes. Each entry must be a CIDR with no host bits set (e.g. `192.168.1.0/24`). `0.0.0.0/0` and `::/0` are rejected; use `advertise_exit_node` instead. Pass `[]` to clear all advertised routes.
exit_node string IP or MagicDNS name of the peer to use as an exit node, or `""` to clear. Under userspace-networking mode a non-empty value is rejected with 409 since the node cannot install host routes to redirect its own traffic through a peer.
exit_node_allow_lan_access boolean
ssh boolean

PasswordRequest

Name Type Description
password string

PeerInfo

Name Type Description
Active boolean
DNSName string
ExitNode boolean True if this peer is advertising itself as an available exit node.
Hostname string
IPv4 string
IPv6 string
LastSeen string(date-time)
Online boolean
OS string
Relay string
TailscaleIPs Array<string>
UserEmail string

PollResult

Name Type Description
connected boolean
timestamp integer(int64) Unix timestamp (seconds).

ServeErrorResult

Name Type Description
message string
status string

ServePendingResult

Name Type Description
message string
status string

ServeRelay

Name Type Description
autostart boolean
enabled boolean
funnel_transport string Only meaningful when `type` is `funnel`.
icon_url string Optional icon rendered on the relay card instead of the default type glyph. Accepts an `http`/`https` URL or a small `data:image/*` URI (capped at 256 KiB to avoid bloating `serve_relays.json`). Empty/omitted means "use the default glyph". Other schemes (e.g. `javascript:`) are rejected with 400.
id string
listen_port integer
target_host string
target_https boolean
target_port integer
type string

ServeRelayWithRunning

Type:

ServeResult

Name Type Description
message string
status string

StatusSummary

Name Type Description
ActivePeers integer
BackendState string
Connected boolean
Health Array<string>
Hostname string
IPv4 string
IPv6 string
LastCheck string(date-time)
MagicDNSName string
PeerCount integer
TailnetName string
Version string

SuccessFlag

Name Type Description
success boolean

SystemStatus

Name Type Description
services Properties: webui, tailscale, serve
timestamp string(date-time)
version string Hardcoded server-reported version string.

TailscaleError

Name Type Description
message string
status string

TailscaleLoginResult

Name Type Description
auth_url string(uri)
message string
status string

TailscaleMessage

Name Type Description
message string
status string

TCPRelayStatus

Name Type Description
relay ServeRelay
running boolean

ToggleRequest

Name Type Description
enabled boolean
id string

Common responses

This section describes common responses that are reused across operations.

Unauthorized

Missing or invalid credentials

{
    "error": "unauthorized"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "error": {
            "type": "string",
            "examples": [
                "unauthorized"
            ]
        }
    },
    "required": [
        "error"
    ]
}

PlainTextError

Error (plain-text body via http.Error)

"string"
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "string"
}

ServeSuccess

Operation succeeded

{
    "status": "success",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "success"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

ServePending

Tailscale not yet ready; relay config saved and applied when connected

{
    "status": "pending",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "pending"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

ServeNotFound

Relay not found

"string"
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "string"
}

ServeMethodError

Method not allowed (plain text)

"string"
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "string"
}

FunnelNotAllowed

Tailscale Funnel not permitted for this device (missing funnel node attribute)

{
    "status": "error",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "error"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

TailscaleError

Tailscale operation error

{
    "status": "error",
    "message": "string"
}
⚠️ This example has been generated automatically from the schema and it is not accurate. Refer to the schema for more information.

Schema of the response body
{
    "type": "object",
    "properties": {
        "status": {
            "type": "string",
            "examples": [
                "error"
            ]
        },
        "message": {
            "type": "string"
        }
    },
    "required": [
        "status",
        "message"
    ]
}

Common parameters

This section describes common parameters that are reused across operations.

RelayId

Name In Type Default Nullable Description
id query string No

Filename

Name In Type Default Nullable Description
filename query string No

Security schemes

Name Type Scheme Description
bearerToken http bearer Static token from `/var/lib/tailscale/.webui_token`.
sessionCookie apiKey Session cookie set after setup/login or successful Tailscale poll.

Tags

Name Description
Info Build metadata (public)
Auth First-run setup, password login, session management
Status Aggregate system status and configured targets
Tailscale Tailscale daemon control and status
Serve HTTPS HTTPS relays via tailscale serve
Serve TCP TCP relays via tailscale serve
Serve Funnel Public relays via tailscale funnel (ports 443, 8443, 10000)
Serve Cross-cutting serve operations
Backup Configuration backup create/restore/manage
Logs Container log history, live stream, and level control