hit counter
Developers

Build on wg0.

Automate a device-first WireGuard control plane. Manage networks, devices, memberships, VPN-through-host, relay assist, and peer state from our hosted API, OpenAPI spec, PATs, or your AI operator tooling.

YOUR AUTOMATION curl Shell scripts cron Jobs · CI claude AI agent OpenAPI generated SDK HTTPS · JSON HOSTED BY WG0 connect.wg0.io /networks /nodes /enroll /provision /traffic /upstream-exits /api-keys /heartbeat REST · OpenAPI 3.1 · MCP YOUR NETWORKS home-mesh 12 devices office 28 devices branch-3 5 devices Isolated per account
The short version

You automate. We orchestrate.

wg0 hosts the control plane. You write scripts, cron jobs, or agents against our public API and manage your networks, devices, and exits programmatically. Sign up, mint a personal access token in the dashboard, and you're one curl away from your first API call. No self-hosted brain, no vendor-lock-in boilerplate.

Current model

Devices first. Nodes are memberships.

The current wg0 control plane is no longer just a flat list of nodes. A managed installation is a device. Each device can have one or more network memberships, and each membership is still represented as a node. The public API keeps node compatibility, but the management model is now device-first.

Relay assist is shipped. Networks can now set relay_mode = never | auto | always on both native and overlay networks. Relay assist helps managed peers recover from degraded direct peering. It does not turn the relay into a public web-browsing exit; internet egress still happens on the host node or its configured BYO Exit.
Five-minute quick start

From zero to your first API call

  1. Sign up for a free account

    Head to login.wg0.io/register. Email + password, no credit card. The free tier includes 3 networks and 10 devices — plenty to build and test against.

  2. Mint a personal access token

    In the dashboard, click API keys → + New token. Give it a name like my-automation, set an expiry if you want, and click Mint token. Copy the value — it's shown once. Tokens start with wg0_pat_ so secret scanners can catch leaks.

  3. Verify the token works

    Send any authenticated request. The base URL is https://connect.wg0.io. The token goes in the Authorization header as a bearer.

  4. Explore the endpoints

    The OpenAPI spec documents every endpoint, request shape, and response shape. Feed it to openapi-generator-cli, orval, or your codegen of choice for a typed client. Or keep reading for a curated endpoint map and copy-pasteable recipes below.

  5. Build something

    Automate onboarding. Nightly health audits. Provision a mobile device from chat. Upload BYO Exits from a Mullvad account switcher. The building blocks are below.

Authentication

One bearer token, two flavors

Every authenticated endpoint accepts a bearer token. The brain detects whether it's a short-lived session JWT from a browser login or a long-lived personal access token and routes accordingly.

Header Scheme Used by
Authorization: Bearer <JWT> Session JWT Humans via the dashboard (login flow)
Authorization: Bearer wg0_pat_… Personal Access Token Scripts, cron, MCP, third-party integrations
X-Device-Secret: <secret> Per-device auth Connectors talking to device protocol endpoints
PAT management is JWT-only, by design. Minting, listing, and revoking tokens on /api/v1/api-keys all reject personal access tokens with a 401. A leaked PAT cannot mint successor credentials or revoke its siblings — it can only do the API work you created it for. To manage tokens, log in through the dashboard (or /api/v1/auth/login) and use the resulting JWT.
Endpoint map

Every endpoint, grouped by purpose

Base URL: https://connect.wg0.io. Full request and response shapes live in the OpenAPI spec.

Auth public

  • POST/api/v1/auth/register— create account, returns JWT
  • POST/api/v1/auth/login— exchange email + password for JWT
  • POST/api/v1/auth/accept-invite— accept an invited account membership

Personal access tokens JWT only

  • POST/api/v1/api-keys— mint a PAT (plaintext shown once)
  • GET/api/v1/api-keys— list PATs (prefix + metadata, never plaintext)
  • DELETE/api/v1/api-keys/:id— revoke a PAT instantly

Accounts and membership context JWT or PAT

  • GET/api/v1/accounts/me— current account, plan, usage, and billing state
  • GET/api/v1/accounts/me/memberships— every account the current user belongs to
  • POST/api/v1/accounts/switch— swap active account context

Networks JWT or PAT

  • GET/api/v1/networks— list networks with counts and traffic totals
  • POST/api/v1/networks— create overlay or native-LAN network
  • PATCH/api/v1/networks/:id— update mutable policy like relay_mode
  • DELETE/api/v1/networks/:id— destroy a network and its memberships
  • GET/api/v1/networks/:id/traffic— aggregated network traffic

Devices JWT or PAT

  • GET/api/v1/devices— managed device inventory
  • GET/api/v1/devices/:id— one managed device with memberships
  • PATCH/api/v1/devices/:id— device-level updates like display name and telemetry collection
  • GET/api/v1/devices/:id/endpoint-history— device-scoped endpoint timeline
  • GET/api/v1/devices/:id/peerings— mesh and relay-helper view
  • POST/api/v1/devices/:id/memberships— request attach to another network
  • PATCH/api/v1/devices/:id/memberships/:node_id— membership-scoped update including VPN-through-host

Enrollment and provisioning JWT or PAT

  • POST/api/v1/enroll/generate-token— mint an enrollment token
  • POST/api/v1/enroll/register— bring-your-own-key enrollment, returns device_id and installation_id
  • POST/api/v1/networks/:id/provision— server-side keygen for QR / mobile flow

Nodes and device protocol v1 mixed

  • GET/api/v1/nodes— node/membership list with presence, host-exit health, and mesh mode
  • PATCH/api/v1/nodes/:id— compatibility path for node settings and BYO Exit selection
  • POST/api/v1/nodes/:id/heartbeat— connector heartbeat (now includes capabilities, installation id, host LAN IP)
  • GET/api/v1/nodes/:id/config— refreshed config render
  • POST/api/v1/nodes/:id/rotate-secret— rotate device secret

Strict v2 device and installation surface device secret + v2 header

  • POST/api/v2/devices/:id/state— current state report + desired state response
  • GET/api/v2/devices/:id/state— desired-state poll
  • PATCH/api/v2/devices/:id/settings— strict device-side VPN toggle
  • GET/api/v2/installations/:id/state— installation-scoped membership inventory
  • POST/api/v2/installations/:id/memberships/:request_id/fulfill— fulfill a pending additional membership

BYO Exit, self-service, and billing JWT or PAT

  • GET/api/v1/nodes/:id/upstream-exits— list exits attached to a host
  • POST/api/v1/nodes/:id/upstream-exits— upload a provider config
  • GET/api/v1/accounts/device-profiles— access profiles and self-service policy
  • GET/api/v1/my-access— member self-service device view
  • GET/api/v1/billing/status— billing readiness probe
  • POST/api/v1/billing/remediate— restrict resources back within plan caps

Health public

  • GET/health— liveness probe, no auth
The fastest way to work with the current platform is: networks + devices + memberships for management, nodes for compatibility, and strict v2 for connector-side state convergence. If you want a clean rendered guide instead of scanning endpoint groups, use the API reference page.
Copy-pasteable recipes

Common tasks, working code

All examples assume BRAIN=https://connect.wg0.io and PAT=wg0_pat_… in your shell.

1Create a network

Overlay network — the brain picks a subnet from our pool.

# POST /api/v1/networks curl -sf -X POST "$BRAIN"/api/v1/networks \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{"name":"home-mesh"}'

Native-LAN network — the brain bridges remote devices into your existing LAN subnet.

curl -sf -X POST "$BRAIN"/api/v1/networks \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{ "name": "home-lan", "network_type": "native", "native_gateway_subnet": "192.168.1.0/24", "native_host_tunnel_ip": "192.168.1.2", "native_client_start": "192.168.1.200", "native_client_end": "192.168.1.220" }'

1bTurn on relay assist and inspect device peerings

Relay assist is a network setting. Device peerings is where you inspect what the control plane decided for each managed membership.

# Put a network in auto relay-assist mode curl -sf -X PATCH "$BRAIN"/api/v1/networks/$NET_ID \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{"relay_mode":"auto"}' # List managed devices, pick one, then inspect mesh / relay state DEVICE_ID=$(curl -sf -H "Authorization: Bearer $PAT" "$BRAIN"/api/v1/devices \ | jq -r '.[0].id') curl -sf -H "Authorization: Bearer $PAT" \ "$BRAIN"/api/v1/devices/$DEVICE_ID/peerings | jq .

2Enroll a device with the wg0 connector

Mint an enrollment token, then run the connector install script on any Linux, macOS, or Docker host.

# 1. Get a network id from the list NET_ID=$(curl -sf -H "Authorization: Bearer $PAT" "$BRAIN"/api/v1/networks \ | jq -r '.[0].id') # 2. Mint an enrollment token ENR=$(curl -sf -X POST "$BRAIN"/api/v1/enroll/generate-token \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d "{\"network_id\":\"$NET_ID\"}" \ | jq -r .token_value) # 3a. Linux client (one-liner) curl -sSf https://wg0.io/connector.sh | sudo bash -s -- enroll "$ENR" https://connect.wg0.io # 3b. Linux host (native-LAN gateway — advertises the LAN subnet) # Order: enroll TOKEN BRAIN_URL NODE_NAME OS_TYPE ROLE ADVERTISED_ROUTES sudo ./connector.sh enroll "$ENR" https://connect.wg0.io $(hostname -s) linux host 10.0.0.0/24 # 3c. macOS (same enroll subcommand) curl -sSf https://wg0.io/connector-macos.sh | sudo bash -s -- enroll "$ENR" https://connect.wg0.io # 3d. Docker host (requires --network host + --privileged for forwarding + NAT) docker run -d --name wg0 --restart unless-stopped \ --network host --privileged \ -v wg0-data:/etc/wireguard \ -e ENROLLMENT_TOKEN="$ENR" \ -e BRAIN_URL=https://connect.wg0.io \ -e ROLE=host -e ADVERTISED_ROUTES=10.0.0.0/24 \ wg0-connector:latest

The Linux host enroll auto-configures IP forwarding, rp_filter, iptables FORWARD rules, and NAT MASQUERADE so remote WG clients can reach devices on the physical LAN. Use standard Docker on normal hosts with kernel WireGuard support; switch to Docker-compat when that kernel path is missing or unreliable. Run sudo ./connector.sh check after enroll to verify.

2bConnector subcommands

Everything connector.sh and the wg0 status CLI expose.

# connector.sh — management sudo ./connector.sh enroll TOKEN https://connect.wg0.io [NAME] [OS] [ROLE] [ROUTES_CSV] sudo ./connector.sh unenroll [BRAIN_URL] # fully remove node + all rules sudo ./connector.sh check # health-check forwarding config (exit 0/1) sudo ./connector.sh update https://connect.wg0.io # migrate to a new brain URL sudo ./connector.sh rotate-secret # rotate device secret (v2 protocol) # wg0 — status CLI (installed to /usr/local/bin/wg0 on enroll) wg0 status # tunnel, peers, heartbeat — full view wg0 status --json # machine-readable JSON for scripts/monitoring wg0 peers # peer list with TX/RX wg0 logs # last 40 lines of heartbeat log wg0 version # connector + tunnel version info

check verifies all six traps that can break host forwarding — ip_forward, rp_filter, proxy_arp (on wg0, not all), FORWARD rules, NAT MASQUERADE, FORWARD policy — and prints the exact fix command for any failure. Exit 0 if healthy, 1 if not.

3Provision a phone with a QR code

Server-side keygen. The brain returns a full wg0.conf string; pipe it through qrencode to print a scannable QR to your terminal.

curl -sf -X POST "$BRAIN"/api/v1/networks/$NET_ID/provision \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{"node_name":"pixel","os_type":"android","role":"client"}' \ | jq -r .wg_config \ | qrencode -t ANSI

Scan the QR with the stock WireGuard app for a provisioned peer. If you want the managed Android app instead, use the dashboard enrollment QR flow rather than this manual config recipe.

4Toggle route-all on a client

Flip a native-LAN client to full-VPN mode. Takes effect on the connector's next heartbeat (~30 seconds).

curl -sf -X PATCH "$BRAIN"/api/v1/nodes/$NODE_ID \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d '{"route_all_traffic": true}'

5Upload a Mullvad exit and activate it

Upload the provider's WireGuard config to a host, then PATCH the host's current_upstream_exit_id to activate.

# Upload EXIT_ID=$(curl -sf -X POST "$BRAIN"/api/v1/nodes/$HOST_ID/upstream-exits \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg wg \"$(cat mullvad-se.conf)\" \ '{name:\"Mullvad SE\", provider_type:\"mullvad\", wg_config:\$wg}')" \ | jq -r .id) # Activate curl -sf -X PATCH "$BRAIN"/api/v1/nodes/$HOST_ID \ -H "Authorization: Bearer $PAT" \ -H "Content-Type: application/json" \ -d "{\"current_upstream_exit_id\":\"$EXIT_ID\"}"

Next heartbeat, every route-all client on that host inherits the upstream. The dashboard shows upstream health within ~30 seconds.

6Audit presence across every network

A nightly health check — list networks, list nodes, filter to anything offline.

curl -sf -H "Authorization: Bearer $PAT" "$BRAIN"/api/v1/nodes \ | jq '[.[] | select(.presence=="offline") | {network: .network_id, name: .node_name, last_activity}]'

Each row has presence (one of online, observed, offline, unknown), observed_endpoint (real post-NAT endpoint from the discover sidecar), and last_activity (most recent signal from any source).

7Rotate a personal access token

PAT management requires a JWT, not a PAT. Log in first, then mint a replacement, then revoke the old one.

# 1. Exchange email + password for a JWT JWT=$(curl -sf -X POST "$BRAIN"/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"[email protected]","password":"…"}' \ | jq -r .access_token) # 2. Mint a replacement PAT NEW=$(curl -sf -X POST "$BRAIN"/api/v1/api-keys \ -H "Authorization: Bearer $JWT" \ -H "Content-Type: application/json" \ -d '{"name":"monitoring-cron-v2","expires_at":"2027-01-01T00:00:00Z"}' \ | jq -r .api_key) # 3. Revoke the old one curl -sf -X DELETE -H "Authorization: Bearer $JWT" \ "$BRAIN"/api/v1/api-keys/$OLD_KEY_ID
Error handling

Structured responses, sensible codes

Every error response is valid JSON with a detail field describing what went wrong. Your client can read it and retry with corrected input.

CodeMeaning
400Malformed request body or missing required field
401Missing or invalid auth — check your bearer token
404Resource not found or not owned by your account
409Conflict — e.g. duplicate subnet or email already registered
422Validation error — e.g. plan cap hit, guardrail violated
500Something broke on our side — file it at [email protected]
# Example 422 response when a Free account tries to create a 4th network HTTP/1.1 422 Unprocessable Entity Content-Type: application/json { "detail": "Your plan includes 3 networks. Contact [email protected] to upgrade (higher tiers and custom limits are available)." }
Beyond the REST API

Connectors and MCP server

For the common install path you don't touch the device protocol directly — our managed apps and connectors do it for you.

⇣

Connector install scripts

One-line install at https://wg0.io/connector.sh (Linux) and connector-macos.sh (macOS shell), plus a native Windows app bundle at /downloads/wg0-windows-portable.zip. The script paths stay available as advanced fallbacks; the native apps are the primary user-facing install surface now.

⌬

MCP server (early access)

Stdio Model Context Protocol server wrapping 16 wg0 operations as tools, 3 resources, and 4 pre-baked prompts. Works with Claude Desktop, Claude Code, Cursor, and any MCP stdio client. Point it at a PAT and your coding agent can inspect and operate your fleet. Email [email protected] for early access until the public install ships.

⊕

Device protocol v1 + v2

Used by the connectors, not by your automation. Heartbeats carry per-device telemetry; v2 adds versioning and strict authentication. Full spec available to enterprise integrators building custom connectors — contact [email protected].

Start building against wg0

Free account, no credit card, 3 networks and 10 devices to play with. The API is the same whether you're on Free or Enterprise — only the caps move.

© 2026 wg0 · WireGuard is a registered trademark of Jason A. Donenfeld Built on WireGuard · [email protected] · [email protected] Web Application Services Provided by ABSG.tech