Update monitor
const url = 'https://app.tunnelhq.com/api/v1/servers/srv_42';const options = { method: 'PUT', headers: { 'X-Project-Id': 'prj_12', 'X-API-Key': '<X-API-Key>', 'Content-Type': 'application/json' }, body: '{"active":false}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url https://app.tunnelhq.com/api/v1/servers/srv_42 \ --header 'Content-Type: application/json' \ --header 'X-API-Key: <X-API-Key>' \ --header 'X-Project-Id: prj_12' \ --data '{ "active": false }'Changes only the fields you send. Needs a role that can edit monitors (Owner, Admin, or Manager).
tagsreplaces the monitor’s tags;[]removes them all.- Sending any configuration field (
url,config,host,username,password,port,proto,remote_id,ip,servers,wg,wireguard_config,wireguard_ip) re-validates the configuration, with the fields you send merged over the stored ones. - Sending a different
protocolreplaces the configuration: send every field the new protocol needs. - For an Uptime check, a field its type doesn’t have is refused with
400and a message naming it, such as “port is for port checks.” A check’s type can’t be changed: the answer is “Create a new check instead”. A heartbeat takesperiod,grace,name,tags, andactive, and its address never changes. See Uptime checks in the API for which fields each type takes. activepauses (false) or resumes (true) the monitor, as the dashboard’s Pause and Resume buttons do: the pause time is kept, the history shows “Monitoring paused” or “Monitoring resumed”, and themonitor.pausedandmonitor.resumedwebhooks fire. It needs permission to pause monitors as well as to edit them. Sending the value the monitor already has changes nothing.- Setting
activetotruecan be refused: when the plan allows no monitors, when a tunnel that TunnelHQ paused after a plan downgrade needs a slot and every tunnel slot is in use (a tunnel you paused keeps its slot, and an Uptime check never needs one), when the monitor’s subscription is switched off, or when the provider no longer lists the server.
Need an API key? See Getting an API key.
Authorizations
Section titled “Authorizations”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters”The monitor, as srv_42 or 42.
Example
srv_42Header Parameters
Section titled “Header Parameters”The project, as prj_12 or 12. A workspace key can only reach its own workspace’s projects.
Example
prj_12Request Bodyrequired
Section titled “Request Bodyrequired”Every field is optional; send only what changes.
object
Seconds between scheduled checks, at least the plan’s minimum.
0 sets the default, 60.
0 sets the default, 2.
false pauses the monitor; true resumes it.
Replaces the monitor’s tags.
A different protocol replaces the configuration; send every field it needs.
object
Keyword and DNS checks. For a DNS check, "" or null clears it.
Keyword checks.
Website and keyword checks.
DNS checks.
Website and keyword checks. A change applies to the project’s other website and keyword checks on the same host.
Website and keyword checks. A change applies to the project’s other website and keyword checks on the same host.
Website, keyword, ping, and TCP port checks. null for both.
Heartbeats.
Heartbeats.
Examples
Pause
{ "active": false}Rename and retag
{ "name": "fra-wg-01-backup", "tags": [ "backup" ]}New password
{ "password": "<new-password>"}Responses
Section titled “ Responses ”The monitor after the change.
object
vpn for a tunnel, or the Uptime check’s type (push is a heartbeat).
healthy: the latest check passed. With several locations, at least one is up and none is down.down: confirmed down. With several locations, at least one is down and none is up.partial: several locations, some up and some down.degraded: the latest check has no verdict yet (retrying, inconclusive, or a fault on TunnelHQ’s checker), or the server refused the monitor’s credentials. The dashboard shows the first case as Unknown and the second as Degraded.paused: the monitor is paused.unknown: the monitor hasn’t been checked yet. The dashboard shows it as Pending.
Always the same as status.
false when the monitor is paused.
Seconds between scheduled checks.
The latest check’s time, in milliseconds. For a tunnel, one round trip through the tunnel. For an Uptime check: the response time (website and keyword), the average round trip (ping), the TCP connect time (TCP port), the query time (DNS, null unless the server answered every question), or the ping value the job sent (heartbeat). Null when that check failed or measured none.
Tunnels only: check rows don’t have it. How long the tunnel took to come up in its latest check, in milliseconds. Null when that check failed or measured none. For VLESS, VMess, Trojan, Shadowsocks, Hysteria2, and TUIC, it’s the time to first contact with the server.
Where the latest check ran from, as any or a lowercase country code. null before the first check.
The locations the monitor tests from. ["any"] unless specific locations were chosen.
Locations that are paused. Never includes any.
The latest result per location, keyed by location code. {} before the first check.
object
object
pending means no verdict yet; degraded, that the server refused the credentials;
unavailable, that no checker is available there, so the location is left out of the status.
When that location last checked, in ISO 8601 UTC. Empty if unknown.
Why the check failed, when it did.
Present only when the check ran through a relay.
Whether a partial outage alerts.
Tag names.
When the latest check ran, in UTC, as YYYY-MM-DD HH:MM:SS with no time zone marker.
Present only on monitors imported from a subscription URL. One monitor stands for every config in the URL that points at the same server.
object
The latest result for each config.
object
The config’s name in the subscription.
The config’s protocol, such as vless.
The address traffic left from, when the check passed.
The same fields per location.
object
Uptime checks only: the address a website or keyword check requests.
Uptime checks only: the host a ping or TCP port check reaches, or the DNS server a DNS check asks.
Uptime checks only: the domain a DNS check asks about. null for other checks.
Uptime checks only: the TCP port check’s port, or 53 for DNS.
Uptime checks only: the text a keyword check looks for, or the text a DNS answer must contain.
Uptime checks only: true when a keyword check alerts on finding the keyword.
Uptime checks only: status codes that count as up, such as 200-299.
Uptime checks only: the certificate the latest check saw, or null before one was seen.
object
Uptime checks only: days before the certificate expires to alert. null is off.
Uptime checks only: days before the domain expires to alert. null is off.
Uptime checks only: ipv4 or ipv6, or null for both.
Heartbeats only: the longest time between two calls from the job, in seconds. interval shows the same value.
Heartbeats only: how late a call can be before the heartbeat is down, in seconds.
Heartbeats only, and only from Create, Get, and Update monitor, to callers who can edit monitors: the address the job calls. Treat it as a secret.
Example
{ "id": "srv_42", "name": "fra-wg-01", "type": "vpn", "protocol": "wireguard", "status": "healthy", "aggregate_status": "healthy", "active": true, "interval": 180, "latency_ms": 157, "connect_ms": 364, "region": "any", "locations": [ "any" ], "disabled_locations": [], "regional_status": { "any": { "status": "up", "last_check": "2026-09-25T14:30:02.000Z", "ping_ms": 212 } }, "notify_on_partial": false, "tags": [ "production" ], "last_checked": "2026-09-25 14:30:02"}Headers
Section titled “Headers”Requests the workspace’s plan allows per minute.
Requests left in the current minute.
Unix time, in seconds, when the current minute ends.
A field is invalid.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 400, "message": "Invalid server ID"}{ "error": true, "code": 400, "message": "Invalid name"}{ "error": true, "code": 400, "message": "interval must be a positive integer (seconds)"}{ "error": true, "code": 400, "message": "Maximum check interval is 86400s (requested 90000s)"}{ "error": true, "code": 400, "message": "Cannot update VPN fields: existing monitor has no protocol and none was provided."}The API key is missing, wrong, expired, switched off, or no longer valid.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 401, "message": "API key is required. Provide X-API-Key header or Authorization: Bearer <key>"}{ "error": true, "code": 401, "message": "Invalid or expired API key"}{ "error": true, "code": 401, "message": "API key has expired"}{ "error": true, "code": 401, "message": "API key owner account is inactive"}{ "error": true, "code": 401, "message": "This API key's workspace access has been removed"}The role or the plan doesn’t allow it.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 403, "message": "You do not have permission to edit this monitor"}{ "error": true, "code": 403, "message": "Minimum check interval on your plan is 300s"}{ "error": true, "code": 403, "message": "Your plan allows 20 tunnels and 20 are in use. Paused tunnels still count, so pausing another will not free a slot — upgrade, or delete a tunnel you no longer need."}{ "error": true, "code": 403, "message": "This workspace's plan does not allow running monitors. Choose a plan to resume monitoring."}{ "error": true, "code": 403, "message": "This workspace's plan could not be read, so monitoring cannot start. Try again, and contact support if it keeps happening."}The project has no such monitor, or the project doesn’t exist.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 404, "message": "Server not found"}{ "error": true, "code": 404, "message": "Project not found or inactive"}The monitor can’t be changed this way right now.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": true, "code": 409, "message": "This monitor's subscription is disabled. Re-enable the subscription (PUT /api/v1/subscriptions/:id with {\"enabled\": true}) before activating its monitors."}{ "error": true, "code": 409, "message": "This server is no longer in the subscription (the provider renamed or removed it), so it can't be resumed — the next sync would just re-pause it. Delete it if it's gone for good; if the provider restores it, you'll be able to resume it again."}A configuration field is longer than its limit.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Example
{ "error": true, "code": 413, "message": "Field 'password' exceeds max length (4096 bytes). Got 5000 bytes."}The new configuration can’t be used.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Example
{ "error": true, "code": 422, "message": "Invalid VPN configuration: …"}A usage limit was reached, or this IP address made too many requests with a wrong key.
Usage limits are per workspace and plan: per minute (Retry-After: 60), per day
(Retry-After: 3600), and per month (no Retry-After). They use their own body shape.
An IP address may send 10 wrong keys a minute; one more is allowed every 6 seconds. While
that allowance is used up, every request from the address is refused, even one with a
valid key.
object
Seconds to wait. Absent for the monthly limit.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Examples
{ "error": "Rate limit exceeded", "message": "Per-minute limit of 60 requests exceeded", "retry_after": 60}{ "error": "Daily limit exceeded", "message": "Daily limit of 25000 requests exceeded", "retry_after": 3600}{ "error": "Monthly limit exceeded", "message": "Monthly limit of 250000 requests exceeded"}{ "error": true, "code": 429, "message": "Too many failed API key attempts from this address. Wait a few seconds and try again.", "retry_after": 6}Headers
Section titled “Headers”Seconds to wait. 60 for the per-minute limit, 3600 for the daily limit, 6 after wrong keys. Absent for the monthly limit.
Something failed on TunnelHQ’s side.
object
The HTTP status, repeated.
What went wrong.
More detail, on some errors.
Seconds to wait, on the 429 for wrong keys.
Example
{ "error": true, "code": 500, "message": "Internal server error"}