---
title: TestingBot API Documentation. Use our API to retrieve info on tests
description: API to access your Selenium test results. Find out which tests fail on
  which browsers.
source_url:
  html: https://testingbot.com/support/api
  md: https://testingbot.com/support/api/index.md
---
# TestingBot API Documentation

Access and modify all your TestingBot data through our comprehensive REST API. Build powerful integrations and automate your testing workflows.

 Endpoint api.testingbot.com

 Version v1

 Format JSON

 Auth HTTP Basic

[Download OpenAPI 2.0 spec (JSON)](https://testingbot.com/api/v1/openapi.json)

## API Clients

There are several open source client libraries available to easily interact with the TestingBot API:

- [NodeJS](https://github.com/testingbot/testingbot-api)
- [Python](https://github.com/testingbot/testingbotclient)
- [Java](https://github.com/testingbot/testingbot-java)
- [Ruby](https://github.com/testingbot/testingbot_ruby)
- [PHP](https://github.com/testingbot/testingbot-php)

## Authentication

Every TestingBot API request is authenticated with your **API key** and **API secret** using [HTTP Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication) over HTTPS. The only unauthenticated endpoint is [GET /v1/browsers](https://testingbot.com#browsers).

### 1 · Find your credentials

Sign in to TestingBot and open [Account → Account Info](https://testingbot.com/members/user/edit). You'll find:

`key` stringYour public API client key. Safe to share between your CI and the TestingBot grid; not a secret on its own.

`secret` string sensitiveTreat this like a password. Never commit it to git, never paste it into screenshots, never embed it in client-side code. Use environment variables or a secret manager in CI.

### 2 · Send credentials on every request

Pass the credentials as the standard `Authorization` header. Most HTTP clients accept a `user:password` tuple and handle the Base64 encoding for you — examples on the right.

### 3 · Verify it works

Hit [GET /v1/user](https://testingbot.com#user). A JSON response with your name and plan means auth is set up correctly. A `401 Unauthorized` means the key or secret is wrong.

**Best practice.** Store credentials in environment variables — `TESTINGBOT_KEY` and `TESTINGBOT_SECRET` — and inject them into your CI/CD pipeline as secrets. Rotate the secret immediately if it leaks (Account → Reset API credentials). 

`Authentication Examples`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Authenticated request

    $ curl https://api.testingbot.com/v1/user \
      -u "$TESTINGBOT_KEY:$TESTINGBOT_SECRET"

    require 'testingbot'
    api = TestingBot::Api.new(
      ENV['TESTINGBOT_KEY'],
      ENV['TESTINGBOT_SECRET']
    )
    api.get_user_info

    import os, testingbotclient
    tb = testingbotclient.TestingBotClient(
      os.environ['TESTINGBOT_KEY'],
      os.environ['TESTINGBOT_SECRET']
    )
    tb.user.get_user_information()

    $api = new TestingBot\TestingBotAPI(
      getenv('TESTINGBOT_KEY'),
      getenv('TESTINGBOT_SECRET')
    );
    $api->getUserInfo();

    TestingbotREST restApi = new TestingbotREST(
      System.getenv("TESTINGBOT_KEY"),
      System.getenv("TESTINGBOT_SECRET")
    );
    TestingbotUser user = restApi.getUserInfo();

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: process.env.TESTINGBOT_KEY,
      api_secret: process.env.TESTINGBOT_SECRET
    });
    
    const userInfo = await api.getUserInfo();

GET`/v1/browsers`

## List supported browsers
 Returns every browser environment available on the TestingBot grid: name, version, platform, and the `browser_id` used when attaching browsers to Codeless tests. No authentication required. 
### Arguments

`type`string Filter by automation protocol: "webdriver" (default) or "rc" (legacy Selenium RC). 

### Response fields

`selenium_name`string Capability value to send as `browserName` in WebDriver. 

`name`string Human-readable browser identifier (e.g. "firefox", "iexplore"). 

`version`integer Major version number. 

`long_version`string Full version string (e.g. "121.0.6167.184"). 

`platform`string Operating system (e.g. "WINDOWS", "MAC", "LINUX"). 

`browser_id`integer Unique TestingBot browser ID used to attach browsers to Codeless tests. 

GET`/v1/browsers`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    curl "https://api.testingbot.com/v1/browsers"

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_browsers

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.information.get_browsers()

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getBrowsers();

    TestingbotREST restApi = new TestingbotREST(key, secret);
    ArrayList<TestingbotBrowser> browsers = restApi.getBrowsers();

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const browsers = await api.getBrowsers();

Response

    [
      {"selenium_name": "IE8", "name": "iexplore", "version": 8, "platform": "WINDOWS", "browser_id": 1, "long_version": 8},
      {"selenium_name": "FF7", "name": "firefox", "version": 7, "platform": "WINDOWS", "browser_id": 22, "long_version": 7}
    ]

GET`/v1/user`

## Get your user info
 Returns the data associated with the authenticated account: name, plan, billing details, remaining credits, and concurrency caps. 
### Response fields

`first_name`string Given name on the account. 

`last_name`string Family name on the account. 

`email`string Account email address. 

`plan`string Subscription plan identifier (e.g. "live", "automated", "automated pro"). 

`max_concurrent`integer Maximum number of parallel VM-based sessions allowed by the plan. 

`max_concurrent_mobile`integer Maximum number of parallel physical device sessions allowed by the plan. 

`seconds`integer Remaining test seconds (credit balance) on the account. 

`last_login`timestamp ISO-8601 timestamp of the most recent dashboard login. 

`company`string Optional company name for billing. 

`street`string Billing address line. 

`city`string Billing city. 

`country`string Billing country. 

`vat`string VAT number (EU only). 

GET`/v1/user`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/user" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_user_info

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.user.get_user_information()

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getUserInfo();

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotUser user = restApi.getUserInfo();

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const userInfo = await api.getUserInfo();

Response

    {
        "first_name": "Steven",
        "last_name": "King",
        "seconds": 17745,
        "plan": "small",
        "max_concurrent": 10,
        "max_concurrent_mobile": 2,
        "company": "companyName",
        "street": "companyStreet",
        "city": "companyCity",
        "country": "companyCountry",
        "vat": "ifInEurope"
    }

PUT`/v1/user`

## Update your user info
 Updates the authenticated account. Only `first_name` and `last_name` are mutable through this endpoint; other profile fields require the dashboard. 
### Arguments

`user[first_name]`string New given name. 

`user[last_name]`string New family name. 

### Response fields

`success`boolean Whether the update was applied. 

`user`object Updated user object. 

PUT`/v1/user`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/user" \
    -X PUT \
    -d "user[first_name]=new" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.update_user_info({ "first_name" => 'new' })

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.user.update_user_information()

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->updateUserInfo(array('first_name' => 'new'));

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotUser user = restApi.updateUserInfo(TestingBotUser);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const newUserData = { first_name: 'new' };
    await api.updateUserInfo(newUserData);

Response

    {
      "success": true,
      "user": {
        "first_name": "new",
        "last_name": "King",
        "seconds": 17745,
        "last_login": "2011-08-06T16:47:04Z"
      }
    }

GET`/v1/team-management`

## Get team concurrency info
 Returns allowed vs current concurrent session counts for the team across both VMs and physical mobile devices. Useful for dashboards that surface "X of Y slots in use". 
### Response fields

`concurrency`object Allowed vs current concurrency caps for the team, split in VM and physical device sessions. 

GET`/v1/team-management`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/team-management" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const teamInfo = await api.getTeam();

Response

    {
      "concurrency": {
        "allowed": { "vms": 10, "physical": 2 },
        "current": { "vms": 4, "physical": 1 }
      }
    }

GET`/v1/team-management/users`

## List users in your team
 Paginated list of all sub-accounts in the team. Requires admin role on the calling account. 
### Arguments

`offset`integer Skip this many users from the start of the result set. 

`count`integermax=`500` Number of users to return . 

### Response fields

`data`array of team member objects Team member accounts. 

`meta`meta object—

GET`/v1/team-management/users`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/team-management/users" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const users = await api.getUsersInTeam();

Response

    {
      "data": [
        {
          "id": 337,
          "first_name": "test",
          "last_name": "user",
          "seconds": 12000,
          "plan": "Free Trial",
          "max_concurrent": 2,
          "max_concurrent_mobile": 2
        }
      ],
      "meta": { "offset": 0, "count": 1, "total": 1 }
    }

GET`/v1/team-management/users/:id`

## Get a specific team user
 Returns the user record for a specific team member. Admins can fetch any team member; non-admins can only fetch themselves. 
### Arguments

`id`integerrequired Numeric ID of the team user to fetch. 

### Response fields

`id`integer Unique numeric user ID. 

`first_name`string Given name. 

`last_name`string Family name. 

`email`string Account email. 

`credits`integer Remaining VM credit seconds. 

`device_credits`integer Remaining physical-device credit seconds. 

`isPaid`boolean Whether the account has an active paid plan. 

`verified`boolean Whether the account email has been verified. 

`parent_id`integer Parent (team owner) user ID; 0 means this user is the team owner. 

GET`/v1/team-management/users/:id`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/team-management/users/:id" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const user = await api.getUserFromTeam(userId);

Response

    {
      "id": 337,
      "first_name": "test",
      "last_name": "user",
      "seconds": 12000,
      "plan": "Free Trial",
      "max_concurrent": 2,
      "max_concurrent_mobile": 2
    }

POST`/v1/team-management/users`

## Create a user in your team
 Provisions a new sub-account inside the team, copying the caller's subscription level and assigning a slice of the credit pool. Requires an upgraded plan and admin role. 
### Arguments

`email`stringrequired Email address for the new account; must be unique across TestingBot. 

`password`stringrequired Initial password for the new user. 

`first_name`string New user's given name. 

`last_name`string New user's family name. 

`concurrency`integer Max parallel VM sessions (≤ team owner's `maxParallel`). 

`concurrencyPhysical`integer Max parallel physical-device sessions (≤ team owner's `maxParallelDevice`). 

POST`/v1/team-management/users`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X POST -u key:secret \
      https://api.testingbot.com/v1/team-management/users \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "first_name": "Bruno",
        "last_name": "Mars",
        "email": "bmars@example.com",
        "password": "$bmaRs*RULES"
      }'

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const userData = {
      first_name: 'John',
      last_name: 'Doe',
      email: 'john@example.com',
      password: 'StrongPassword!'
    };
    
    const result = await api.createUserInTeam(userData);

Response

    {
      "id": 337,
      "first_name": "Bruno",
      "last_name": "Mars",
      "seconds": 12000,
      "plan": "Free Trial",
      "max_concurrent": 2,
      "max_concurrent_mobile": 2
    }

PUT`/v1/team-management/users/:id`

## Update a user in your team
 Updates a team user's profile and credit allocation. Credit fields cannot exceed the team owner's remaining balance. 
### Arguments

`id`integerrequired Numeric ID of the team user to update. 

`first_name`string New given name. 

`last_name`string New family name. 

`email`string New email address. 

`password`string New password. 

`credits`integer Allocated VM credit seconds (≤ team owner's remaining VM credits). 

`device_credits`integer Allocated physical-device credit seconds (≤ team owner's remaining device credits). 

`concurrency`integer Max parallel VM sessions for this user. 

`concurrencyPhysical`integer Max parallel physical-device sessions for this user. 

### Response fields

`id`integer Unique numeric user ID. 

`first_name`string Given name. 

`last_name`string Family name. 

`email`string Account email. 

`credits`integer Remaining VM credit seconds. 

`device_credits`integer Remaining physical-device credit seconds. 

`isPaid`boolean Whether the account has an active paid plan. 

`verified`boolean Whether the account email has been verified. 

`parent_id`integer Parent (team owner) user ID; 0 means this user is the team owner. 

PUT`/v1/team-management/users/:id`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X PUT -u key:secret \
      https://api.testingbot.com/v1/team-management/users/:id \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "first_name": "Bruno",
        "last_name": "Mars"
      }'

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const result = await api.updateUserInTeam(userId, { first_name: 'Jane', last_name: 'Smith' });

Response

    {
      "id": 337,
      "first_name": "Bruno",
      "last_name": "Mars",
      "seconds": 12000,
      "plan": "Free Trial"
    }

POST`/v1/team-management/users/:id/reset-keys`

## Reset credentials for a team user
 Rotates both the API key and secret for a team user. The old credentials stop working immediately, so any deployed automation using them must be updated. 
### Arguments

`id`integerrequired Numeric ID of the team user whose credentials to rotate. 

POST`/v1/team-management/users/:id/reset-keys`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X POST -u key:secret \
      https://api.testingbot.com/v1/team-management/users/:id/reset-keys

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const result = await api.resetCredentials(userId);

Response

    {
      "success": true,
      "client_key": "d69e0800xc32ed2f059b7a630ee255b"
    }

GET`/v1/tests`

## List your tests
 Paginated list of every test session belonging to the authenticated account, newest first. Filter by `browser_id`, `group`, or `build` to narrow the result. Use `since` to fetch only tests updated after a UNIX timestamp (poll-friendly). 
### Arguments

`offset`integer Skip this many tests from the start of the result set. 

`count`integermax=`500` Number of tests to return . 

`since`integer UNIX timestamp; return only tests updated at or after this time. 

`browser_id`integer Filter to tests that ran on this browser\_id (from /v1/browsers). 

`group`string Filter to tests tagged with this group name. 

`build`string Filter to tests in this build (matches `capabilities.build`). 

`skip_fields`string Comma-separated fields to omit (logs, thumbs). 

### Response fields

`data`array of test case objects Test sessions for this page. 

`meta`meta object—

GET`/v1/tests`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tests?offset=0&count=10" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_tests(0, 10)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.tests.get_tests(offset=0, limit=10)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getJobs(0, 10);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotTest test = restApi.getTests(0, 10);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const tests = await api.getTests({ offset, limit });

Response

    {
      "data": [
        {
          "created_at": "2011-07-30T23:21:23Z",
          "completed_at": "2011-07-30T23:22:44Z",
          "id": 3,
          "name": "MyTest::testTitle",
          "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
          "status_message": "Failed asserting that 1 equals 0.",
          "status_id": 0,
          "success": false,
          "browser": "iexplore",
          "browser_version": 8,
          "os": "WINDOWS",
          "duration": 13,
          "build": "buildid",
          "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4",
          "groups": ["testingbot.com"]
        }
      ],
      "meta": { "offset": 0, "count": 10, "total": 789 }
    }

GET`/v1/tests/:id`

## Get a specific test
 Returns the full detail record for a single test session: status, environment (browser/OS or device/platform), assets (video, logs, screenshots), groups, build, and duration. Accepts either the numeric test ID or the WebDriver session\_id. 
### Arguments

`id`stringrequired Numeric test ID or WebDriver session\_id (UUID). 

`skip_fields`string Comma-separated fields to omit from the response (logs, thumbs, visual\_run, groups). 

### Response fields

`id`integer Unique numeric test ID. 

`session_id`string Selenium / WebDriver session ID (UUID). 

`name`string Human-readable test name set via capabilities or `update_test`. 

`state`string Lifecycle state (RUNNING, COMPLETE, TIMEOUT, …). 

`success`boolean Whether the test passed. 

`status_id`integer Numeric status code (0=fail, 1=pass, 2=unknown). 

`status_message`string Failure reason or arbitrary status set via `test[status_message]`. 

`created_at`timestamp When the test session started. 

`completed_at`timestamp When the session ended; null while running. 

`duration`integer Total run time in seconds. 

`browser`string Browser identifier (e.g. "iexplore", "firefox"). 

`browser_version`integer Browser major version. 

`os`string OS (e.g. "WINDOWS", "MAC"). 

`device_name`string Mobile device name when the session ran on a physical device; null otherwise. 

`platform_name`string Mobile OS name; null on desktop. 

`build`string Build identifier (free-form string set via capabilities). 

`groups`array of string Tag/group names attached to the test. 

`video`string Signed S3 URL to the recorded video, or false if video was disabled. 

`thumbs`array of string Signed S3 URLs to screenshot thumbnails. 

`logs`object Map of log names → signed S3 URLs (selenium, browser, …). 

`assets_available`boolean Whether assets (video, logs, screenshots) have finished processing. 

`extra`string Arbitrary metadata string set via `test[extra]`. 

`type`string Driver type (WEBDRIVER, APPIUM). 

GET`/v1/tests/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tests/:id" -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_test(test_id)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.tests.get_test(test_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getJob($test_id);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotTest test = restApi.getTest(test_id);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const test = await api.getTestDetails(testId);

Response

    {
      "id": 3,
      "name": "MyTest::testTitle",
      "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
      "state": "COMPLETE",
      "success": false,
      "status_id": 0,
      "status_message": "Failed asserting that 1 equals 0.",
      "created_at": "2011-07-30T23:21:23Z",
      "completed_at": "2011-07-30T23:22:23Z",
      "duration": 60,
      "browser": "iexplore",
      "browser_version": 8,
      "os": "WINDOWS",
      "device_name": null,
      "platform_name": null,
      "build": null,
      "groups": ["testingbot.com"],
      "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4",
      "thumbs": ["https://s3.amazonaws.com/euthumbtestingbot/3_f93782fji.jpg"],
      "logs": { "selenium": "https://s3-eu-west-1.amazonaws.com/eulogtestingbot/session.txt" },
      "assets_available": true,
      "type": "WEBDRIVER"
    }

PUT`/v1/tests/:id`

## Update a test
 Updates a test's metadata after it's been recorded. Use this to mark a test as passed/failed from the test runner, attach groups/tags, set a build identifier, or add a status message. Accepts either a numeric test ID or a WebDriver session\_id. 
### Arguments

`id`stringrequired Numeric test ID or WebDriver session\_id (UUID). 

`test[name]`string Human-readable test name. 

`test[success]`boolean true=pass, false=fail. 

`test[status_message]`string Failure reason or arbitrary status text. 

`test[extra]`string Arbitrary metadata. 

`test[build]`string Build identifier to associate this test with. 

`test[public]`boolean When true, makes the test publicly viewable via share URL. 

`groups`string Comma-separated tag/group names to attach to the test. 

`build`string Build identifier (alternative location to `test[build]`). 

PUT`/v1/tests/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tests/:id" \
    -X PUT \
    -d "test[success]=1" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.update_test(test_id, { name: 'new_name', success: true })

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.tests.update_test(test_id, status_message='..', passed=1, build='..', name='..')

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->updateJob($test_id, ['name' => 'mytest', 'success' => true]);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    boolean success = restApi.updateTest(testId, details);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    await api.updateTest({ 'test[success]': '1', 'test[status_message]': 'failure reason' }, testId);

Response

    {
      "success": true
    }

DELETE`/v1/tests/:id`

## Delete a test
 Permanently deletes a test session and every associated asset (video, logs, screenshots). This action cannot be undone. 
### Arguments

`id`stringrequired Numeric test ID or WebDriver session\_id of the test to delete. 

DELETE`/v1/tests/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tests/:id" \
    -X DELETE \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.delete_test(test_id)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.tests.delete_test(test_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->deleteJob($test_id);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    boolean success = restApi.deleteTest(testId);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    await api.deleteTest(testId);

Response

    {
      "success": true
    }

PUT`/v1/tests/:id/stop`

## Stop a running test
 Terminates an in-flight test session. The test is marked complete; any assets gathered (video, logs, screenshots) become available for download. Returns 404 if the test has already finished. 
### Arguments

`id`stringrequired Numeric test ID or WebDriver session\_id of the test to stop. 

PUT`/v1/tests/:id/stop`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tests/:id/stop" \
    -X PUT \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.stop_test(test_id)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.tests.stop_test(test_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->stopJob($test_id);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    boolean success = restApi.stopTest(testId);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    await api.stopTest(testId);

Response

    {
      "success": true
    }

GET`/v1/builds`

## List your builds
 Returns a paginated list of test builds. A build is an aggregation of test cases that share the same `capabilities.build` identifier, useful for grouping CI runs. 
### Arguments

`offset`integer Skip this many builds from the start of the result set. 

`count`integermax=`500` Number of builds to return . 

### Response fields

`data`array of build objects Builds for this page. 

`meta`meta object—

GET`/v1/builds`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/builds?offset=0&count=10" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_builds(0, 10)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.build.get_builds(offset=0, limit=10)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getBuilds(0, 10);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotBuildCollection builds = restApi.getBuilds(0, 10);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const builds = await api.getBuilds(offset, limit);

Response

    {
      "data": [
        {
          "id": 4331,
          "build_identifier": "first-build",
          "created_at": "2016-08-01T11:09:02.000Z",
          "updated_at": "2016-08-01T11:09:02.000Z"
        }
      ],
      "meta": { "offset": 0, "count": 10, "total": 3 }
    }

GET`/v1/builds/:id`

## Get tests for a build
 Returns all test cases that belong to a single build, with pagination. The build can be referenced by either its numeric internal ID or the string identifier you set via `capabilities.build`. 
### Arguments

`id`stringrequired Numeric build ID or string build identifier. 

`offset`integer Skip this many tests in the build. 

`count`integermax=`500` Number of tests to return . 

`skip_fields`string Comma-separated fields to omit from each test (logs, thumbs). 

### Response fields

`data`array of test case objects Test sessions for this page. 

`meta`meta object—

GET`/v1/builds/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/builds/:id" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_build(build_identifier)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.build.get_tests_for_build(build_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getBuild($build_id);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotTestBuildCollection tests = restApi.getTestsForBuild(buildId);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const tests = await api.getTestsForBuild(buildId);

Response

    {
      "data": [
        {
          "id": 3,
          "name": "MyTest::testTitle",
          "session_id": "f7903f9e93e74fe1b0e924bf9d2ce9fc",
          "state": "COMPLETE",
          "success": false,
          "status_id": 0,
          "created_at": "2011-07-30T23:21:23Z",
          "completed_at": "2011-07-30T23:22:44Z",
          "duration": 13,
          "browser": "iexplore",
          "browser_version": 8,
          "os": "WINDOWS",
          "build": "buildid",
          "video": "https://s3.amazonaws.com/rectestingbot/sample.mp4"
        }
      ],
      "meta": { "offset": 0, "count": 10, "total": 1 }
    }

DELETE`/v1/builds/:id`

## Delete a build
 Permanently deletes a build and every test, asset (video, logs, screenshots) attached to it. This action cannot be undone. 
### Arguments

`id`stringrequired Numeric build ID or string build identifier to delete. 

DELETE`/v1/builds/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/builds/:id" \
    -X DELETE \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.delete_build(build_identifier)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.build.delete_build(build_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->deleteBuild($build_id);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    boolean success = restApi.deleteBuild(buildId);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    await api.deleteBuild(buildId);

Response

    {
      "success": true
    }

GET`/v1/devices`

## List physical mobile devices
 Returns every real Android and iOS device in the TestingBot grid, with an `available` flag indicating whether it can be acquired right now. Filter by `platform` for a single OS family. 
### Arguments

`platform`string Filter to a single mobile OS family. 

`web`boolean Internal flag used by the dashboard — returns extra UI-only fields. 

### Response fields

`id`integer Unique numeric device ID. 

`name`string Marketing name (e.g. "iPhone 15 Pro"). 

`model`string Device model identifier. 

`manufacturer`string OEM (e.g. "Apple", "Samsung"). 

`platform_name`string Mobile OS family (e.g. "iOS", "Android"). 

`platform_version`string OS version (e.g. "17.4", "14"). 

`screen_size`string Display size in inches (e.g. "6.1"). 

`screen_resolution`string Display resolution (e.g. "1170x2532"). 

GET`/v1/devices`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -u key:secret "https://api.testingbot.com/v1/devices"

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_devices

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.information.get_devices()

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getDevices();

    TestingbotREST restApi = new TestingbotREST(key, secret);
    ArrayList<TestingbotDevice> devices = restApi.getDevices();

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const devices = await api.getDevices();

Response

    [
      {
        "id": 1,
        "available": true,
        "name": "Galaxy S8",
        "model_number": "SM-G950U1",
        "platform_name": "Android",
        "version": "9.0",
        "resolution": "1080x2220",
        "multiple_browsers": [
          { "id": 1727, "name": "chrome", "version": "103" },
          { "id": 2133, "name": "firefox", "version": "98.2" },
          { "id": 2953, "name": "samsung", "version": "13.2.1.70" },
          { "id": 2954, "name": "opera", "version": "67.1" }
        ]
      },
      {
        "id": 5,
        "available": false,
        "name": "iPhone XR",
        "platform_name": "iOS",
        "version": "16.3",
        "resolution": "828x1792"
      }
    ]

GET`/v1/devices/available`

## List currently available devices
 Subset of /v1/devices filtered to devices the authenticated account can acquire right now (not in use by another customer, not under maintenance). 
### Response fields

`id`integer Unique numeric device ID. 

`name`string Marketing name (e.g. "iPhone 15 Pro"). 

`model`string Device model identifier. 

`manufacturer`string OEM (e.g. "Apple", "Samsung"). 

`platform_name`string Mobile OS family (e.g. "iOS", "Android"). 

`platform_version`string OS version (e.g. "17.4", "14"). 

`screen_size`string Display size in inches (e.g. "6.1"). 

`screen_resolution`string Display resolution (e.g. "1170x2532"). 

GET`/v1/devices/available`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -u key:secret "https://api.testingbot.com/v1/devices/available"

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_available_devices

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.information.get_available_devices()

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getAvailableDevices();

    TestingbotREST restApi = new TestingbotREST(key, secret);
    ArrayList<TestingbotDevice> devices = restApi.getAvailableDevices();

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const devices = await api.getAvailableDevices();

Response

    [
      {
        "id": 1,
        "available": true,
        "name": "Galaxy S8",
        "platform_name": "Android",
        "version": "9.0",
        "resolution": "1080x2220"
      }
    ]

GET`/v1/devices/:id`

## Get a specific device
 Returns the spec and current availability for a single device by numeric ID. 
### Arguments

`id`integerrequired Numeric device ID. 

### Response fields

`id`integer Unique numeric device ID. 

`name`string Marketing name (e.g. "iPhone 15 Pro"). 

`model`string Device model identifier. 

`manufacturer`string OEM (e.g. "Apple", "Samsung"). 

`platform_name`string Mobile OS family (e.g. "iOS", "Android"). 

`platform_version`string OS version (e.g. "17.4", "14"). 

`screen_size`string Display size in inches (e.g. "6.1"). 

`screen_resolution`string Display resolution (e.g. "1170x2532"). 

GET`/v1/devices/:id`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/devices/:id" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_device(device_id)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.information.get_device(device_id)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getDevice($deviceId);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotDevice device = restApi.getDevice(deviceId);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const device = await api.getDevice(deviceId);

Response

    {
      "id": 12,
      "available": true,
      "name": "Galaxy S20",
      "platform_name": "Android",
      "version": "10.0",
      "resolution": "1440x3200",
      "multiple_browsers": [
        { "id": 2598, "name": "chrome", "version": "101" },
        { "id": 2919, "name": "firefox", "version": "98.2" },
        { "id": 2957, "name": "samsung", "version": "13.2.1.70" }
      ]
    }

POST`/v1/screenshots`

## Capture a new screenshot batch
 Queues a cross-browser screenshot of a given URL at a specified resolution. Returns a batch ID you can poll via GET /v1/screenshots/:id. 
### Arguments

`url`stringrequired Page URL to screenshot. 

`resolution`stringrequired Browser viewport (e.g. "1920x1080"). 

`browsers`arrayrequired Array of `{ browser, version, os }` triples (or `browser_id`s) to render with. 

`wait_time`integer Seconds to wait after page load before snapping. 

`fullpage`boolean Capture the entire scrollable page instead of just the viewport. 

`callback_url`string POST callback URL invoked when the batch finishes processing. 

POST`/v1/screenshots`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -H 'Content-Type: application/json' -X POST "https://api.testingbot.com/v1/screenshots" \
    -u key:secret \
    -d '{
      "url": "https://www.google.com",
      "resolution": "1280x1024",
      "browsers": [
        { "browserName": "chrome", "version": 134, "os": "WIN10" },
        { "browserName": "safari", "version": "17.2", "platformName": "iOS", "deviceName": "iPhone 15" }
      ]
    }'

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const screenshots = await api.takeScreenshot(
      'https://example.com',
      [{ browserName: 'chrome', version: 'latest', os: 'WIN11' }],
      '1920x1080'
    );

Response

    {
      "id": 3454,
      "wait_time": 0,
      "resolution": "1280x1024",
      "url": "https://www.google.com"
    }

GET`/v1/screenshots/{id}`

## Get screenshot batch detail
 Returns per-browser screenshot results for a single batch, including thumbnail/image URLs and processing state. 
### Arguments

`id`integerrequired Numeric screenshot batch ID. 

`excludeIds`string Comma-separated screenshot IDs to exclude (useful for delta-fetch). 

### Response fields

`id`integer Unique screenshot job ID. 

`url`string URL the screenshot was taken from. 

`state`string Processing state. 

`wait_time`integer Seconds the browser waited before snapping. 

`resolution`string Browser viewport resolution. 

`screenshots`array of object Per-browser screenshot results with download URLs. 

`created_at`timestamp—

GET`/v1/screenshots/{id}`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/screenshots/{id}" -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const screenshotResult = await api.retrieveScreenshots(screenshotJobId);

Response

    {
      "state": "done",
      "screenshots": [
        {
          "image_url": "https://....",
          "thumb_url": "https://....",
          "state": "done",
          "id": "9c7deea4-6f23-4041-842b-39cccee447fa",
          "created_at": "2017-10-13T14:47:44.000Z",
          "os": "SEQUOIA",
          "browser_name": "googlechrome",
          "browser_version": "138",
          "browser_id": 1146
        }
      ]
    }

GET`/v1/screenshots`

## List screenshot batches
 Returns batches of cross-browser screenshots the account has queued historically. 
### Arguments

`offset`integer Skip this many batches. 

`count`integer Number of batches to return. 

### Response fields

`data`array of screenshot objects—

`meta`meta object—

GET`/v1/screenshots`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/screenshots" -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const screenshots = await api.getScreenshotList();

Response

    {
      "data": [
        { "id": 3454, "url": "https://google.com", "resolution": "1280x1024" },
        { "id": 3452, "url": "https://testingbot.com", "resolution": "1280x1024" }
      ],
      "meta": { "offset": 0, "count": 3, "total": 3 }
    }

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. 

`state`string Tunnel lifecycle state (READY, STOPPED, …). 

`launched`timestamp When the tunnel was launched. 

`tunnel_id`string Public tunnel identifier surfaced in the dashboard. 

`identifier`string Custom name passed via --tunnel-identifier on the client. 

`metadata`object Free-form metadata (client version, OS, region) reported by the tunnel binary. 

GET`/v1/tunnel/list`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tunnel/list" \
    -u key:secret

    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,
        "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`integerrequired Numeric tunnel ID to stop. 

DELETE`/v1/tunnel/{id}`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/tunnel/{id}" \
    -X DELETE \
    -u key:secret

    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/lab`

## List your Codeless tests
 Paginated list of every Codeless test (a.k.a. Lab test) on the account. Codeless tests are recorded in TestingBot's in-browser recorder and can be scheduled on a cron, fired via API, or chained into suites. 
### Arguments

`offset`integer Skip this many tests from the start of the result set. 

`count`integermax=`500` Number of tests to return . 

### Response fields

`data`array of lab test objects—

`meta`meta object—

GET`/v1/lab`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const codelessTests = await api.getCodelessTests();

Response

    {
      "data": [
        {
          "id": 215,
          "enabled": false,
          "alerts": [],
          "url": "https://testingbot.com/",
          "name": "testingbot",
          "created_at": "2012-03-12T20:03:45Z",
          "updated_at": "2012-03-13T20:26:43Z",
          "last_run": "2012-03-13T06:50:48Z",
          "browsers": [
            { "name": "firefox", "version": 10, "os": "LINUX" }
          ]
        }
      ],
      "meta": { "offset": 0, "count": 10, "total": 25 }
    }

GET`/v1/lab/{id}`

## Get a specific Codeless test
 Returns a single Codeless test's configuration: schedule, alerts, attached browsers, and timestamps. Use the steps endpoint to retrieve the actual test recording. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

### Response fields

`id`integer Unique numeric Codeless test ID. 

`name`string Test name. 

`url`string Target URL the test runs against. 

`enabled`boolean Whether scheduled runs are active. 

`cron`string Cron expression for scheduled runs; null if unscheduled. 

`created_at`timestamp—

`updated_at`timestamp—

`last_run`timestamp Most recent run timestamp. 

`alerts`array of lab alert objects Configured alert destinations. 

`browsers`array of browser objects Browsers this test is configured to run on. 

GET`/v1/lab/{id}`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab/{id}" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const codelessTest = await api.getCodelessTest(labTestId);

Response

    {
      "id": 215,
      "enabled": false,
      "alerts": [],
      "url": "https://testingbot.com/",
      "name": "testingbot",
      "created_at": "2012-03-12T20:03:45Z",
      "updated_at": "2012-03-13T20:26:43Z",
      "last_run": "2012-03-13T06:50:48Z",
      "browsers": [
        { "name": "firefox", "version": 10, "os": "LINUX" }
      ]
    }

POST`/v1/lab`

## Create a Codeless test
 Creates a new Codeless test. Pass `test[name]` and either `test[url]` (target URL the test visits) or `file` (Selenium IDE export to import). Optional `test[ai_prompt]` lets you describe what the AI test agent should verify in plain English. 
### Arguments

`test[name]`string Test name. 

`test[url]`string Target URL to test (required if no `file` is uploaded). 

`test[cron]`string Cron expression for scheduled runs. 

`test[screenshot]`boolean Take screenshots at every step. 

`test[video]`boolean Record a video of the test. 

`test[idletimeout]`integer Seconds of idle time before the runner aborts the test. 

`test[screenresolution]`string Browser viewport (e.g. "1920x1080"). 

`test[ai_prompt]`string Plain-English instruction for the AI test agent. 

`file`file Selenium IDE `.side` export to import as the test's steps. 

POST`/v1/lab`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab" \
    -u key:secret \
    -d "test[name]=test" \
    -d "test[cron]=31 * * * *"

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const result = await api.createCodelessTest({
      name: 'My Codeless Test',
      url: 'https://example.com',
      ai_prompt: 'Test the login flow',
      cron: '0 0 * * *',
      screenshot: true,
      video: false,
      idletimeout: 60,
      screenresolution: '1920x1080'
    });

Response

    {
      "success": true,
      "lab_test_id": 59392
    }

DELETE`/v1/lab/{id}`

## Delete a Codeless test
 Permanently deletes a Codeless test and its steps. Test runs in history remain. 
### Arguments

`id`integerrequired Numeric Codeless test ID to delete. 

DELETE`/v1/lab/{id}`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab/{id}" \
    -X DELETE \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const result = await api.deleteCodelessTest(testId);

Response

    {
      "success": true
    }

PUT`/v1/lab/{id}`

## Update a Codeless test
 Updates a Codeless test's metadata: name, target URL, cron schedule, enabled state. Accepts either a numeric Codeless test ID or a WebDriver session\_id of a test run that originated from this Codeless test. 
### Arguments

`id`stringrequired Numeric Codeless test ID or WebDriver session\_id. 

`test[name]`string New test name. 

`test[url]`string New target URL. 

`test[cron]`string New cron expression. 

`test[enabled]`boolean Enable or pause scheduled runs. 

PUT`/v1/lab/{id}`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab/{id}" \
    -X PUT \
    -d "test[cron]=* * * * *" \
    -u key:secret

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const result = await api.updateCodelessTest({
      test: { name: 'Updated Test Name', cron: '0 12 * * *' }
    }, testId);

Response

    {
      "success": true
    }

POST`/v1/lab/{id}/schedule`

## Set or update a Codeless test schedule
 Schedules a Codeless test to run on a recurring interval. Choose between once / daily / weekly presets or a raw cron expression. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`type`string Schedule preset; use "custom" with `cronFormat` for fine control. 

`day`string Date (YYYY-MM-DD) for "once" or weekday for "weekly". 

`hour`string Time (HH:MM) for "once", "daily", or "weekly". 

`cronFormat`string Raw cron expression (5-field) used when `type=custom`. 

POST`/v1/lab/{id}/schedule`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/schedule" \
    -u key:secret \
    -d "type=daily" -d "hour=00:01"

Response

    {
      "success": true
    }

POST`/v1/lab/{id}/alert`

## Add an alert to a Codeless test
 Adds a notification channel (email, SMS, or callback URL) that fires when a scheduled run fails. Use PUT to modify an existing alert. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`kind`stringrequired Alert channel. 

`level`stringrequired When to send (every failure vs. daily digest). 

`content`stringrequired Destination — email address, callback URL, or phone number. 

POST`/v1/lab/{id}/alert`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/alert" \
    -u key:secret \
    -d "kind=EMAIL" -d "level=IMMEDIATELY" -d "content=alerts@example.com"

Response

    {
      "success": true
    }

POST`/v1/lab/{id}/report`

## Add a daily report config to a Codeless test
 Configures a recurring email report summarising pass/fail rate for this Codeless test. Use PUT to update. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`email`stringrequired Email address that receives the report. 

`cron`string Cron expression for when to send the report (defaults to daily). 

POST`/v1/lab/{id}/report`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/report" \
    -u key:secret \
    -d "email=reports@example.com" -d "cron=0 9 * * *"

Response

    {
      "success": true
    }

POST`/v1/lab/{id}/steps`

## Replace the Codeless test's steps
 Deletes the test's existing steps and replaces them with the provided array. Useful for programmatic editing of recorded tests. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`steps`arrayrequired Ordered list of steps; each is `{ order:, cmd:, locator:, value: }`. 

POST`/v1/lab/{id}/steps`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/steps" \
    -u key:secret \
    -H 'Content-Type: application/json' \
    -d '{ "steps": [{ "order": 0, "cmd": "open", "locator": "/", "value": "" }] }'

Response

    {
      "success": true
    }

GET`/v1/lab/{id}/steps`

## List Codeless test steps
 Returns the recorded Selenium-IDE steps for a Codeless test, with pagination. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`offset`integer Skip this many steps. 

`count`integer Number of steps to return. 

### Response fields

`data`array of lab test step objects—

`meta`meta object—

GET`/v1/lab/{id}/steps`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab/{id}/steps" \
    -u key:secret

Response

    {
      "data": [
        { "test_order": 0, "cmd": "open", "locator": "/", "value": "", "created_at": "2019-05-02T14:33:48.000Z", "updated_at": "2019-05-02T14:33:48.000Z" },
        { "test_order": 1, "cmd": "type", "locator": "id=username", "value": "test", "created_at": "2019-05-02T14:33:48.000Z", "updated_at": "2019-05-02T14:33:48.000Z" },
        { "test_order": 2, "cmd": "type", "locator": "id=password", "value": "test", "created_at": "2019-05-02T14:33:48.000Z", "updated_at": "2019-05-02T14:33:48.000Z" }
      ],
      "meta": { "offset": 0, "count": 3, "total": 3 }
    }

GET`/v1/lab/{id}/browsers`

## Get browsers for a Codeless test
 Returns the list of browsers this Codeless test is configured to run on. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

### Response fields

`selenium_name`string Capability value to send as `browserName` in WebDriver. 

`name`string Human-readable browser identifier (e.g. "firefox", "iexplore"). 

`version`integer Major version number. 

`long_version`string Full version string (e.g. "121.0.6167.184"). 

`platform`string Operating system (e.g. "WINDOWS", "MAC", "LINUX"). 

`browser_id`integer Unique TestingBot browser ID used to attach browsers to Codeless tests. 

GET`/v1/lab/{id}/browsers`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/lab/{id}/browsers" \
    -u key:secret

Response

    [
      { "name": "firefox", "version": "41", "os": "VISTA" }
    ]

POST`/v1/lab/{id}/browsers`

## Update browsers for a Codeless test
 Replaces the entire browser set attached to this Codeless test. Pass `browser_ids` as a comma-separated list of IDs from `/v1/browsers`. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`browser_ids`stringrequired Comma-separated list of browser\_ids the test should run on. 

POST`/v1/lab/{id}/browsers`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/browsers" \
    -u key:secret \
    -d "browser_ids=1,5,12"

Response

    {
      "success": true
    }

POST`/v1/lab/{id}/trigger`

## Run a specific Codeless test
 Triggers an immediate run of a Codeless test on the browsers configured for it. Returns a `job_id` you can poll with `GET /v1/jobs/:id`. 
### Arguments

`id`integerrequired Numeric Codeless test ID to run. 

`url`string Override the test's base URL for this run only. 

POST`/v1/lab/{id}/trigger`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/{id}/trigger" \
    -u key:secret

Response

    {
      "success": true,
      "job_id": 14
    }

PUT`/v1/lab/{id}/stop`

## Stop a running Codeless test
 Force-stops an in-flight Codeless test run. Optional `browser_id` stops only the run on a specific browser. 
### Arguments

`id`integerrequired Numeric Codeless test ID. 

`browser_id`integer Only stop the run on this browser\_id (omit to stop all). 

PUT`/v1/lab/{id}/stop`
[cURL](https://testingbot.com#)
Request

    $ curl -X PUT "https://api.testingbot.com/v1/lab/{id}/stop" \
    -u key:secret

Response

    {
      "success": true
    }

POST`/v1/lab/trigger_all`

## Run all Codeless tests
 Queues a run of every Codeless test on the account. Returns a job\_id that aggregates the results — poll via `GET /v1/jobs/:id`. Optional `url` overrides each test's base URL for this run. 
### Arguments

`url`string Override base URL for every queued test. 

POST`/v1/lab/trigger_all`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/lab/trigger_all" \
    -u key:secret

Response

    {
      "success": true,
      "job_id": 14
    }

GET`/v1/jobs/{id}`

## Get a job's status
 Returns the live status of an asynchronous job — typically used to poll Codeless test/suite runs started by `POST /v1/lab/:id/trigger`, `POST /v1/lab/trigger_all`, or `POST /v1/labsuites/:id/trigger`. Once `status` is FINISHED, the response includes `success` and per-test results. 
### Arguments

`id`integerrequired Numeric job ID returned by a trigger endpoint. 

### Response fields

`status`string Job state (QUEUED, RUNNING, FINISHED, FAILED). 

`created_at`timestamp—

`updated_at`timestamp—

`success`boolean Aggregate pass/fail across all triggered tests; null until FINISHED. 

`test_ids`array of integer Test IDs spawned by this job. 

`errors`array of object Per-test failure detail (step, browser, time). 

GET`/v1/jobs/{id}`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/jobs/{id}" \
    -u key:secret

Response

    {
      "status": "FINISHED",
      "created_at": "2016-04-15T13:47:39.000Z",
      "updated_at": "2016-04-15T13:49:30.000Z",
      "success": false,
      "test_ids": [6620446],
      "errors": [
        {
          "msg": "Actual value 'Google' did not match 'Goooogle'",
          "step": "verifyTitle",
          "browser": { "name": "firefox", "version": "43", "os": "VISTA" },
          "time": "2016-04-15T13:48:25.839Z",
          "test": 6620446
        }
      ]
    }

POST`(your callback URL)`

## Webhook callback
 If you have configured a webhook URL, we will POST the result to the callback URL you configured in the Codeless alerts section. The body below is the JSON payload we send; respond with 2xx to acknowledge. 

POST`(your callback URL)`

Response

    {
        "success": false,
        "errors": [
            {
                "msg": "word Not found",
                "step": "verifyTextPresent",
                "browser": {
                    "name": "iexplore",
                    "version": "8",
                    "os": "WINDOWS"
                },
                "time": "2012-03-13 20:34:59 UTC",
                "test_id": "48586",
                "job_id": 17,
                "lab_id": 133
            }
        ],
        "job_id": 3,
        "test_ids": [48586]
    }

GET`/v1/labsuites`

## List your Codeless suites
 Paginated list of every Codeless suite (a.k.a. Lab suite) on the account. A suite groups several Codeless tests so they can be scheduled, triggered, and reported on as a unit. 
### Arguments

`offset`integer Skip this many suites from the start of the result set. 

`count`integermax=`500` Number of suites to return . 

### Response fields

`data`array of lab suite objects—

`meta`meta object—

GET`/v1/labsuites`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/labsuites" \
    -u key:secret

Response

    {
      "data": [
        {
          "id": 1051,
          "enabled": true,
          "name": "Example",
          "created_at": "2017-03-07T19:11:41.000Z",
          "updated_at": "2017-03-09T12:17:22.000Z",
          "last_run": "2017-03-09T12:17:22.000Z",
          "cron": null,
          "test_count": 5,
          "alerts": [
            { "type": "API", "value": "https://mysite.com/callback/", "level": "IMMEDIATELY" }
          ],
          "browsers": [
            { "name": "firefox", "version": "41", "os": "VISTA" }
          ]
        }
      ],
      "meta": { "offset": 0, "count": 10, "total": 1 }
    }

GET`/v1/labsuites/{id}`

## Get a specific Codeless suite
 Returns a single Codeless suite's configuration: schedule, alerts, attached browsers, and number of tests inside. 
### Arguments

`id`integerrequired Numeric suite ID. 

### Response fields

`id`integer Unique numeric Codeless suite ID. 

`name`string Suite name. 

`enabled`boolean Whether scheduled runs are active. 

`cron`string Cron expression for scheduled runs. 

`test_count`integer Number of Codeless tests attached. 

`created_at`timestamp—

`updated_at`timestamp—

`last_run`timestamp—

`alerts`array of lab alert objects—

GET`/v1/labsuites/{id}`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/labsuites/{id}" \
    -u key:secret

Response

    {
      "enabled": true,
      "name": "Example",
      "cron": null,
      "test_count": 5,
      "created_at": "2017-03-07T19:11:41.000Z",
      "updated_at": "2017-03-09T12:17:22.000Z",
      "last_run": "2017-03-09T12:17:22.000Z",
      "alerts": [
        { "type": "API", "value": "https://mysite.com/callback/", "level": "IMMEDIATELY" }
      ]
    }

POST`/v1/labsuites/{id}/trigger`

## Run a Codeless suite
 Queues every test in the suite for an immediate run. Returns a job\_id you can poll with `GET /v1/jobs/:id`. 
### Arguments

`id`integerrequired Numeric suite ID to trigger. 

POST`/v1/labsuites/{id}/trigger`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/labsuites/{id}/trigger" \
    -u key:secret

Response

    {
      "success": true,
      "job_id": 14
    }

POST`/v1/labsuites`

## Create a Codeless suite
 Creates a new Codeless suite. Attach Codeless tests with `POST /v1/labsuites/:id/tests` after creation. 
### Arguments

`suite[name]`stringrequired Suite name. 

`suite[cron]`string Cron expression for scheduled suite runs. 

`suite[screenshot]`boolean Take screenshots at every step in every test. 

`suite[video]`boolean Record video for tests in this suite. 

`suite[idletimeout]`integer Idle timeout in seconds before tests are aborted. 

`suite[screenresolution]`string Browser viewport for every test in the suite. 

POST`/v1/labsuites`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/labsuites" \
    -u key:secret \
    -d "suite[name]=My Suite"

Response

    {
      "success": true,
      "suite_id": 59391
    }

DELETE`/v1/labsuites/{id}`

## Delete a Codeless suite
 Deletes the suite. The Codeless tests attached to it are not deleted — only the suite grouping. 
### Arguments

`id`integerrequired Numeric suite ID to delete. 

DELETE`/v1/labsuites/{id}`
[cURL](https://testingbot.com#)
Request

    $ curl -X DELETE "https://api.testingbot.com/v1/labsuites/{id}" \
    -u key:secret

Response

    {
      "success": true
    }

GET`/v1/labsuites/{id}/browsers`

## Get browsers for a Codeless suite
 Returns the list of browsers the suite is configured to run on. 
### Arguments

`id`integerrequired Numeric suite ID. 

### Response fields

`selenium_name`string Capability value to send as `browserName` in WebDriver. 

`name`string Human-readable browser identifier (e.g. "firefox", "iexplore"). 

`version`integer Major version number. 

`long_version`string Full version string (e.g. "121.0.6167.184"). 

`platform`string Operating system (e.g. "WINDOWS", "MAC", "LINUX"). 

`browser_id`integer Unique TestingBot browser ID used to attach browsers to Codeless tests. 

GET`/v1/labsuites/{id}/browsers`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/labsuites/{id}/browsers" \
    -u key:secret

Response

    [
      { "name": "firefox", "version": "41", "os": "VISTA" }
    ]

POST`/v1/labsuites/{id}/browsers`

## Update browsers for a Codeless suite
 Replaces the browser set attached to a suite. Every test in the suite will run on the new browser list at next trigger. 
### Arguments

`id`integerrequired Numeric suite ID. 

`browser_ids`stringrequired Comma-separated list of browser\_ids the suite should run on. 

POST`/v1/labsuites/{id}/browsers`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/labsuites/{id}/browsers" \
    -u key:secret \
    -d "browser_ids=1,5,12"

Response

    {
      "success": true
    }

GET`/v1/labsuites/{id}/tests`

## List tests in a Codeless suite
 Returns the Codeless tests attached to a suite, with pagination. 
### Arguments

`id`integerrequired Numeric suite ID. 

`offset`integer Skip this many tests. 

`count`integer Number of tests to return. 

### Response fields

`data`array of lab test objects—

`meta`meta object—

GET`/v1/labsuites/{id}/tests`
[cURL](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/labsuites/{id}/tests" \
    -u key:secret

Response

    {
      "data": [
        {
          "id": 18666,
          "enabled": true,
          "name": "MyTest",
          "url": "https://mysite.com/",
          "created_at": "2017-03-09T12:14:48.000Z",
          "updated_at": "2017-03-09T12:19:30.000Z",
          "last_run": "2017-03-09T12:19:30.000Z",
          "cron": null,
          "browsers": [
            { "name": "firefox", "version": "41", "os": "VISTA" }
          ]
        }
      ],
      "meta": { "offset": 0, "count": 1, "total": 1 }
    }

POST`/v1/labsuites/{id}/tests`

## Add tests to a Codeless suite
 Attaches one or more existing Codeless tests to a suite. Pass `test_ids` as a comma-separated list. 
### Arguments

`id`integerrequired Numeric suite ID. 

`test_ids`stringrequired Comma-separated list of Codeless test IDs to attach. 

POST`/v1/labsuites/{id}/tests`
[cURL](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/labsuites/{id}/tests" \
    -u key:secret \
    -d "test_ids=215,228"

Response

    {
      "success": true
    }

DELETE`/v1/labsuites/{id}/tests/{testid}`

## Remove a test from a Codeless suite
 Detaches a single Codeless test from a suite. The test itself is preserved. 
### Arguments

`id`integerrequired Numeric suite ID. 

`testid`integerrequired Numeric Codeless test ID to detach. 

DELETE`/v1/labsuites/{id}/tests/{testid}`
[cURL](https://testingbot.com#)
Request

    $ curl -X DELETE "https://api.testingbot.com/v1/labsuites/{id}/tests/{testid}" \
    -u key:secret

Response

    {
      "success": true
    }

POST`/v1/storage`

## Upload an app to storage
 Uploads an APK or IPA to TestingBot storage. Either send the binary as multipart `file` or pass a public `url` that TestingBot will fetch. The returned `app_url` (`tb://<appkey>`) can be set as the `app` capability on subsequent mobile sessions. 
### Arguments

`file`file Multipart binary upload of an .apk or .ipa. 

`url`string Public HTTPS URL TestingBot will download the binary from. 

POST`/v1/storage`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/storage" \
    -u key:secret \
    -F "file=@/path/to/app/file/Application-debug.apk"

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.upload_local_file(local_file_path)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.storage.upload_local_file(local_file_path)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->uploadLocalFileToStorage($pathToLocalFile);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingbotStorageUploadResponse uploadResponse = restApi.uploadToStorage(file);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const { app_url } = await api.uploadFile(localFilePath);

Response

    {
      "app_url": "tb://398eijf83i"
    }

POST`/v1/storage/{path}`

## Update an app binary by appkey
 Replaces the binary stored under an existing `appkey`. The `app_url` (`tb://<appkey>`) stays the same so deployed CI configurations keep working without changes — useful for "always use the latest" CI flows. 
### Arguments

`splat`integerrequired—

`file`file Multipart binary replacement. 

`url`string Public HTTPS URL to fetch the new binary from. 

POST`/v1/storage/{path}`
[cURL](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X POST "https://api.testingbot.com/v1/storage/{app_url}" \
    -u key:secret \
    -F "file=@/path/to/app/file/Application-debug.apk"

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const appUrl = await api.uploadFile(localFilePath);

Response

    {
      "app_url": "tb://398eijf83i"
    }

GET`/v1/storage/{path}`

## Get a stored app by appkey
 Returns metadata for a single uploaded app — its filename, type, version, icon, and download URL. 
### Arguments

`splat`integerrequired—

### Response fields

`id`integer Unique numeric storage object ID. 

`app_url`string TestingBot storage URL (`tb://<appkey>`) — pass as a capability to reference this app in mobile tests. 

`url`string Signed HTTPS URL to download the binary directly. 

`filename`string Original uploaded filename. 

`type`string Detected app type (apk, ipa, zip). 

`version`string CFBundleShortVersionString / versionName extracted from the binary. 

`min_device_version`string Minimum OS version required by the app. 

`thumb`string App icon (signed PNG URL). 

`created_at`timestamp Upload timestamp. 

`state`string Processing state (PROCESSING, READY). 

`sim_only`boolean True for iOS simulator-only IPAs (no signing). 

GET`/v1/storage/{path}`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/storage/{app_url}" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_uploaded_file(app_url)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.storage.get_stored_file(app_url)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getStorageFile($appUrl);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingBotStorageFile storedFile = restApi.getStorageFile(appUrl);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    await api.getStorageFile(appUrl);

Response

    {
      "app_url": "tb://0ec522702cd81ec1374e9b3c",
      "url": "http://...",
      "id": 261,
      "type": "ANDROID",
      "filename": "sample.apk",
      "version": "1.0.1",
      "min_device_version": "12.0",
      "thumb": "https://...image.png",
      "created_at": "2019-03-08T08:58:32.000Z"
    }

GET`/v1/storage`

## List your storage apps
 Paginated list of every app you've uploaded to TestingBot storage, newest first. 
### Arguments

`offset`integer Skip this many apps from the start of the result set. 

`count`integermax=`500` Number of apps to return . 

### Response fields

`data`array of storage file objects Storage objects for this page. 

`meta`meta object—

GET`/v1/storage`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/storage/" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.get_uploaded_files(0, 30)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.storage.get_stored_files(offset=0, limit=30)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->getStorageFiles();

    TestingbotREST restApi = new TestingbotREST(key, secret);
    TestingBotStorageFileCollection fileList = restApi.getStorageFiles(0, 30);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const files = await api.getStorageFiles(offset, count);

Response

    [
      {
        "app_url": "tb://….",
        "url": "…",
        "id": 44,
        "type": "ANDROID",
        "filename": "sample.apk",
        "version": "1.0.1",
        "min_device_version": "23",
        "thumb": "https://...image.png",
        "created_at": "2019-01-08T13:42:42.000Z"
      }
    ]

DELETE`/v1/storage/{id}`

## Delete a stored app
 Permanently removes the binary and its metadata. Sessions referencing the `app_url` after deletion will fail at session-start with "app not found". 
### Arguments

`id`stringrequired Numeric storage ID or appkey to delete. 

DELETE`/v1/storage/{id}`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[PHP](https://testingbot.com#)[Java](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl -X DELETE "https://api.testingbot.com/v1/storage/{app_url}" \
    -u key:secret

    require 'testingbot'
    api = TestingBot::Api.new(key, secret)
    api.delete_uploaded_file(file_identifier)

    import testingbotclient
    tb = testingbotclient.TestingBotClient(key, secret)
    tb.storage.remove_file(app_url)

    $api = new TestingBot\TestingBotAPI($key, $secret);
    $api->deleteStorageFile($appUrl);

    TestingbotREST restApi = new TestingbotREST(key, secret);
    boolean success = restApi.deleteStorageFile(appUrl);

    const TestingBot = require('testingbot-api');
    
    const api = new TestingBot({
      api_key: "your-tb-key",
      api_secret: "your-tb-secret"
    });
    
    const success = await api.deleteStorageFile(fileId);

Response

    {
      "success": true
    }

GET`/v1/configuration/ip-ranges`

## TestingBot IP ranges for firewall whitelisting
 Returns the up-to-date list of public IPv4 addresses used by TestingBot test machines. Allow these in your firewall when running tests against staging or production behind corporate VPN/network rules. The response is a flat JSON array of IPv4 strings (no CIDR ranges, no metadata) — re-fetch periodically since the pool changes as we scale capacity. Unauthenticated; no `key`/`secret` required. 

GET`/v1/configuration/ip-ranges`
[cURL](https://testingbot.com#)[Ruby](https://testingbot.com#)[Python](https://testingbot.com#)[NodeJS](https://testingbot.com#)
Request

    $ curl "https://api.testingbot.com/v1/configuration/ip-ranges"

    require 'net/http'
    require 'json'
    
    ips = JSON.parse(Net::HTTP.get(URI('https://api.testingbot.com/v1/configuration/ip-ranges')))
    puts ips

    import requests
    
    ips = requests.get('https://api.testingbot.com/v1/configuration/ip-ranges').json()
    print(ips)

    const res = await fetch('https://api.testingbot.com/v1/configuration/ip-ranges');
    const ips = await res.json();
    console.log(ips);

Response

    [
      "109.68.162.161",
      "109.68.162.165",
      "78.20.187.211",
      "162.55.135.22",
      "95.217.83.184",
      "95.216.39.110",
      "95.216.229.109",
      "142.132.137.7",
      "195.201.167.43",
      "162.55.24.100",
      "185.223.133.13"
    ]
