This episode covers the Tailscale API for managing devices, keys, and ACLs via REST, OAuth apps for device provisioning, plus the Terraform Provider and automated provisioning workflows with scripts and Ansible.

Up to episode 16, most interaction happened through the admin console and CLI. But once a tailnet grows — dozens of nodes, large teams, routine ACL changes — manual operations become slow and error-prone. The answer: automation.
Episode 17 covers Tailscale's programmatic layer: the REST API for managing devices, keys, and ACLs, OAuth apps for secure device provisioning, plus the Terraform Provider and automation workflows with scripts and Ansible.
The Tailscale API is a REST API with endpoints at https://api.tailscale.com/api/v2/. Every admin console operation can be done via the API: listing devices, creating auth keys, reading and writing ACLs. Authentication uses an API key or OAuth token.
curl -s "https://api.tailscale.com/api/v2/tailnet/example.com/devices" \
-H "Authorization: Bearer tskey-api-xxxxx"The curl command above fetches the list of all devices in the tailnet. The response is JSON that scripts can process.
Important endpoints you'll use often:
GET /api/v2/tailnet/{tailnet}/devices: list devices.POST /api/v2/tailnet/{tailnet}/keys: create an auth key.GET /api/v2/tailnet/{tailnet}/acl: read the ACL file.POST /api/v2/tailnet/{tailnet}/acl: write the ACL file.{
"devices": [
{
"id": "trk-example",
"hostname": "web-prod",
"addresses": ["100.101.102.103"]
}
]
}The JSON response above shows the shape of device data: id, hostname, and tailnet address. Your scripts can use it for monitoring or automated inventory.
OAuth apps let integrations use short-lived tokens instead of static, long-lived API keys. The main flow: create an OAuth client in the admin console, get a client ID and secret, then exchange them for a token via the device flow.
curl -s https://api.tailscale.com/api/v2/oauth/token \
-d "client_id=tskey-client-xxxx" \
-d "client_secret=secret" \
-d "grant_type=client_credentials"The response contains an access_token that's valid for a short time. This is a safer pattern for production than hardcoding an API key.
For provisioning devices at scale, OAuth beats static auth keys: tokens can be created per environment with limited scope and a short validity period. Containers and VMs can request a token at startup, add themselves to the tailnet, and the token then expires.
The Tailscale Terraform Provider manages a tailnet declaratively — config files become the single source of truth:
provider "tailscale" {
api_key = var.tailscale_api_key
tailnet = "example.com"
}
resource "tailscale_acl" "default" {
acl = file("acl.hujson")
}The resource "tailscale_acl" block above applies the ACL file to the tailnet. With Terraform, ACL changes are reviewed through pull requests and applied reproducibly.
For more flexibility, combining scripts and Ansible is a common choice:
export TS_AUTHKEY=$(curl -s -X POST \
-H "Authorization: Bearer $API_KEY" \
https://api.tailscale.com/api/v2/tailnet/example.com/keys | jq -r .key)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --auth-key="$TS_AUTHKEY" --hostname=worker-$RANDOMThe script above creates an auth key via the API, then uses the sudo tailscale up --auth-key=... command to provision a new node. With Ansible, this flow runs against many hosts at once, idempotently.
Episode 17 opened the path to full automation: you can manage a tailnet via the REST API, use OAuth for secure provisioning, and define the entire configuration declaratively with Terraform or scripts.
Key takeaways:
In the next episode, episode 18, we'll cover performance and troubleshooting — connection diagnostics with tailscale ping, tailscale netcheck, and tailscale derp, understanding direct versus relay latency, plus common issues like connections after sleep, DERP fallback, DNS not resolving, tailscale debug, and tailscale --bugreport.