Skip to main content

Tunnel

Manage the secure tunnels that let the grid reach your staging and internal environments.

Endpoint
api.testingbot.com
Version
v1
Format
JSON
Auth
HTTP Basic
GET /v1/tunnel/list

List active tunnels

Returns every TestingBot Tunnel currently running under the account. Use this to monitor running tunnels in CI dashboards or to find a tunnel ID before tearing it down.

Response fields

id integer
Unique numeric tunnel ID.
identifier string
The --tunnel-identifier the tunnel was started with, or null when it was started without one. Use this to tell concurrent tunnels on the same account apart.
state string
Tunnel lifecycle state (READY, STOPPED, …).
private_ip string
Internal IP of the tunnel VM.
ip string
Public IP of the tunnel VM.
requested_at timestamp
When the tunnel was requested.
user_id integer
ID of the user the tunnel belongs to.
GET /v1/tunnel/list
Request
$ curl "https://api.testingbot.com/v1/tunnel/list" \
-u key:secret
var client = new TestingBotClient(key, secret);
var tunnels = await client.Tunnels.ListAsync();
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_tunnels
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tunnel.get_tunnels()
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->getTunnels();
TestingbotREST restApi = new TestingbotREST(key, secret);
ArrayList<TestingbotTunnel> tunnels = restApi.getTunnels();
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const tunnels = await api.getTunnelList();
Response
[
  {
    "id": 1,
    "identifier": "my-ci-tunnel",
    "state": "READY",
    "ip": "xx",
    "private_ip": "xx",
    "requested_at": "2026-05-13 01:34:23"
  }
]
DELETE /v1/tunnel/{id}

Stop a tunnel

Tears down a running tunnel. Use this in CI teardown steps to release the slot for the next run; idle tunnels also self-terminate after the configured timeout.

Arguments

id integer required
Numeric tunnel ID to stop.
DELETE /v1/tunnel/{id}
Request
$ curl "https://api.testingbot.com/v1/tunnel/{id}" \
-X DELETE \
-u key:secret
var client = new TestingBotClient(key, secret);
await client.Tunnels.StopAsync(tunnelId);
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.delete_tunnel(tunnel_id)
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.tunnel.delete_tunnel(tunnel_id)
$api = new TestingBot\TestingBotAPI($key, $secret);
$api->deleteTunnel($tunnelID);
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.deleteTunnel(tunnelId);
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.deleteTunnel(tunnelId);
Response
{
  "success": true
}
GET /v1/tunnel

Get the user's active tunnel

Returns the first active tunnel for the authenticated account (regardless of ID). Convenience endpoint when you only run one tunnel at a time.

Response fields

id integer
Unique numeric tunnel ID.
state string
Tunnel lifecycle state (READY, STOPPED, …).
private_ip string
Internal IP of the tunnel VM, used to chain the tunnel with BrowserMob.
ip string
Public IP of the tunnel VM. Only present once state is READY.
version string
Latest tunnel client version TestingBot supports.
GET /v1/tunnel
Request
$ curl "https://api.testingbot.com/v1/tunnel" \
-u key:secret
var client = new TestingBotClient(key, secret);
var tunnel = await client.Tunnels.GetActiveAsync();
$client = new TestingBot\Client($key, $secret);
$tunnel = $client->tunnels()->getActive();
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTunnel tunnel = restApi.getTunnel();
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const tunnel = await api.getTunnel();
DELETE /v1/tunnel

Stop the user's active tunnel

Tears down whichever tunnel is currently active on the account, without needing to specify an ID. Useful when you only ever run one tunnel and want a simple cleanup call.
DELETE /v1/tunnel
Request
$ curl -X DELETE "https://api.testingbot.com/v1/tunnel" \
-u key:secret
var client = new TestingBotClient(key, secret);
await client.Tunnels.StopActiveAsync();
$client = new TestingBot\Client($key, $secret);
$client->tunnels()->deleteActive();
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean success = restApi.deleteTunnel();
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

await api.deleteActiveTunnel();
POST /v1/tunnel/create

Create a tunnel

Launches a new TestingBot Tunnel VM and returns its connection metadata. Most users start tunnels via the tunnel client/CLI rather than this endpoint directly.
POST /v1/tunnel/create
Request
$ curl -X POST "https://api.testingbot.com/v1/tunnel/create" \
-u key:secret
var client = new TestingBotClient(key, secret);
var tunnel = await client.Tunnels.CreateAsync();
$client = new TestingBot\Client($key, $secret);
$tunnel = $client->tunnels()->create();
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTunnel tunnel = restApi.createTunnel();
GET /v1/tunnel/isalive-check

Liveness check

Unauthenticated ping endpoint used by the tunnel binary itself to confirm it can reach the TestingBot API. Always returns { success: true }.

Response fields

success boolean
Whether the operation succeeded.
errors object
Validation errors keyed by field name. Only present when success is false.
error string
Single human-readable reason, used by the older endpoints in place of errors. Only present when success is false.
GET /v1/tunnel/isalive-check
Request
$ curl "https://api.testingbot.com/v1/tunnel/isalive-check" \
-u key:secret
var client = new TestingBotClient(key, secret);
var alive = await client.Tunnels.IsAliveAsync();
TestingbotREST restApi = new TestingbotREST(key, secret);
boolean alive = restApi.isTunnelAlive();
GET /v1/tunnel/{id}

Get a specific tunnel

Returns the current state of a tunnel by ID. Returns 500 with the underlying error if the tunnel is in a transition state or no longer exists.

Arguments

id integer required
Numeric tunnel ID returned by /v1/tunnel/list or /v1/tunnel/create.

Response fields

id integer
Unique numeric tunnel ID.
state string
Tunnel lifecycle state (READY, STOPPED, …).
private_ip string
Internal IP of the tunnel VM, used to chain the tunnel with BrowserMob.
ip string
Public IP of the tunnel VM. Only present once state is READY.
version string
Latest tunnel client version TestingBot supports.
GET /v1/tunnel/{id}
Request
$ curl "https://api.testingbot.com/v1/tunnel/{id}" \
-u key:secret
var client = new TestingBotClient(key, secret);
var tunnel = await client.Tunnels.GetAsync(tunnelId);
$client = new TestingBot\Client($key, $secret);
$tunnel = $client->tunnels()->get($tunnelId);
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTunnel tunnel = restApi.getTunnel(tunnelId);
const TestingBot = require('testingbot-api');

const api = new TestingBot({
  api_key: "your-tb-key",
  api_secret: "your-tb-secret"
});

const tunnel = await api.getTunnelById(tunnelId);
Was this page helpful?
Last updated