---
title: User & Team Management API | TestingBot API Documentation
description: Read and update your TestingBot account, rotate API keys, and manage
  team members and service accounts programmatically.
source_url:
  html: https://testingbot.com/support/api/user
  md: https://testingbot.com/support/api/user.md
---

# User & Team Management

Your own account and credentials, plus everything needed to provision team members and service accounts from a script.

- **Endpoint:** api.testingbot.com
- **Version:** v1
- **Format:** JSON
- **Auth:** [HTTP Basic](https://testingbot.com/support/api#authentication)

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

- **`id` integer:** Unique numeric user ID. Also the path param for team endpoints (e.g. /users/:id) and the parent\_id reference on team members.
- **`first_name` string:** Given name on the account.
- **`last_name` string:** Family name on the account.
- **`seconds` integer:** Remaining test seconds (credit balance) on the account; -1 means unlimited (e.g. Live plans).
- **`last_login` timestamp:** ISO-8601 timestamp of the most recent dashboard login.
- **`plan` string:** Subscription plan name (e.g. "Free Trial", "Live - 5 Sessions - Unlimited", "Automated Pro ..."). For a sub-account this is the team owner's plan.
- **`max_concurrent` integer:** Maximum number of parallel VM-based sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`max_concurrent_mobile` integer:** Maximum number of parallel physical device sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`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).
- **`read_only` boolean:** Whether this account has read-only access within the team.
- **`roles` array of string:** Roles held by the account: "admin" and/or "owner".
- **`current_vm_concurrency` integer:** VM-based sessions this account is running right now.
- **`current_physical_concurrency` integer:** Physical-device sessions this account is running right now.

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

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

```csharp
var client = new TestingBotClient(key, secret);
var user = await client.User.GetAsync();
```

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

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

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

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

```javascript
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

```json
{
    "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. Only present when `success` is true.
- **`errors` object:** Validation errors keyed by field name. Only present when `success` is false.

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

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

```csharp
var client = new TestingBotClient(key, secret);
await client.User.UpdateAsync(new UserUpdate { FirstName = "Bruno", LastName = "Mars" });
```

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

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.user.update_user_information({'user[first_name]': 'new'})
```

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

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

```javascript
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

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

GET `/v1/user/keys`

## Get your API key and secret
 Returns the API client key and secret for the authenticated account. Useful for confirming the credentials currently in use, but never embed this response in client-side code. 
### Response fields

- **`key` string:** API client key.
- **`secret` string:** API client secret. Treat as a password: never embed it in client-side code.

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

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

```csharp
var client = new TestingBotClient(key, secret);
var keys = await client.User.GetKeysAsync();
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_user_keys
```

```php
$client = new TestingBot\Client($key, $secret);
$keys = $client->user()->keys();
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
JsonElement keys = restApi.getUserKeys();
```

```javascript
const TestingBot = require('testingbot-api');

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

const { key, secret } = await api.getUserKeys();
```

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, plus how many sessions are waiting for a free slot. Poll this before fanning out a parallel build. Counts are team-wide and come from the Hub, so they match what the dashboard shows. 
### 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#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

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

```csharp
var client = new TestingBotClient(key, secret);
var concurrency = await client.Team.GetConcurrencyAsync();
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_team
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.get_concurrency()
```

```php
$client = new TestingBot\Client($key, $secret);
$team = $client->teamManagement()->get();
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTeam team = restApi.getTeam();
```

```javascript
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

```json
{
  "concurrency": {
    "allowed": { "vms": 10, "physical": 2 },
    "current": { "vms": 10, "physical": 1 },
    "queued": { "vms": 3, "physical": 0 }
  }
}
```

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` integer max=`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#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

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

```csharp
var client = new TestingBotClient(key, secret);
var members = await client.Team.ListUsersAsync();
```

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

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.get_users(offset=0, limit=10)
```

```php
$client = new TestingBot\Client($key, $secret);
$members = $client->teamManagement()->listUsers(0, 10);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTeamMemberCollection members = restApi.getTeamMembers();
```

```javascript
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

```json
{
  "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` integer required:** Numeric ID of the team user to fetch.

### Response fields

- **`id` integer:** Unique numeric user ID. Also the path param for team endpoints (e.g. /users/:id) and the parent\_id reference on team members.
- **`first_name` string:** Given name on the account.
- **`last_name` string:** Family name on the account.
- **`seconds` integer:** Remaining test seconds (credit balance) on the account; -1 means unlimited (e.g. Live plans).
- **`last_login` timestamp:** ISO-8601 timestamp of the most recent dashboard login.
- **`plan` string:** Subscription plan name (e.g. "Free Trial", "Live - 5 Sessions - Unlimited", "Automated Pro ..."). For a sub-account this is the team owner's plan.
- **`max_concurrent` integer:** Maximum number of parallel VM-based sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`max_concurrent_mobile` integer:** Maximum number of parallel physical device sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`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).
- **`read_only` boolean:** Whether this account has read-only access within the team.
- **`roles` array of string:** Roles held by the account: "admin" and/or "owner".
- **`current_vm_concurrency` integer:** VM-based sessions this account is running right now.
- **`current_physical_concurrency` integer:** Physical-device sessions this account is running right now.

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

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

```csharp
var client = new TestingBotClient(key, secret);
var member = await client.Team.GetUserAsync(userId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.get_user_in_team(user_id)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.get_user(user_id)
```

```php
$client = new TestingBot\Client($key, $secret);
$member = $client->teamManagement()->getUser($userId);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTeamMember member = restApi.getTeamMember(userId);
```

```javascript
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

```json
{
  "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` string required:** Email address for the new account; must be unique across TestingBot.
- **`password` string required:** 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#) [.NET](https://testingbot.com#) [Ruby](https://testingbot.com#) [Python](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

```bash
$ 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"
  }'
```

```csharp
var client = new TestingBotClient(key, secret);
var member = await client.Team.CreateUserAsync(new TeamMemberCreate
{
    Email = "bmars@example.com",
    Password = "$bmaRs*RULES",
    FirstName = "Bruno",
    LastName = "Mars"
});
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.create_user_in_team({ first_name: 'Bruno', last_name: 'Mars', email: 'bmars@example.com', password: '$bmaRs*RULES' })
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.create_user(email='bmars@example.com', password='$bmaRs*RULES', first_name='Bruno', last_name='Mars')
```

```php
$client = new TestingBot\Client($key, $secret);
$member = $client->teamManagement()->createUser([
    'email' => 'bmars@example.com',
    'password' => '$bmaRs*RULES',
    'first_name' => 'Bruno',
    'last_name' => 'Mars',
]);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
Map<String, Object> params = new HashMap<>();
params.put("first_name", "Bruno");
params.put("last_name", "Mars");
params.put("email", "bmars@example.com");
params.put("password", "$bmaRs*RULES");
TestingbotTeamMember member = restApi.createTeamMember(params);
```

```javascript
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

```json
{
  "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` integer required:** 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. Also the path param for team endpoints (e.g. /users/:id) and the parent\_id reference on team members.
- **`first_name` string:** Given name on the account.
- **`last_name` string:** Family name on the account.
- **`seconds` integer:** Remaining test seconds (credit balance) on the account; -1 means unlimited (e.g. Live plans).
- **`last_login` timestamp:** ISO-8601 timestamp of the most recent dashboard login.
- **`plan` string:** Subscription plan name (e.g. "Free Trial", "Live - 5 Sessions - Unlimited", "Automated Pro ..."). For a sub-account this is the team owner's plan.
- **`max_concurrent` integer:** Maximum number of parallel VM-based sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`max_concurrent_mobile` integer:** Maximum number of parallel physical device sessions allowed by the plan. For a sub-account this is the team owner's cap.
- **`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).
- **`read_only` boolean:** Whether this account has read-only access within the team.
- **`roles` array of string:** Roles held by the account: "admin" and/or "owner".
- **`current_vm_concurrency` integer:** VM-based sessions this account is running right now.
- **`current_physical_concurrency` integer:** Physical-device sessions this account is running right now.

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

```bash
$ 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"
  }'
```

```csharp
var client = new TestingBotClient(key, secret);
var member = await client.Team.UpdateUserAsync(userId, new TeamMemberUpdate { FirstName = "Bruno", LastName = "Mars" });
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.update_user_in_team(user_id, { first_name: 'Bruno', last_name: 'Mars' })
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.update_user(user_id, first_name='Bruno', last_name='Mars')
```

```php
$client = new TestingBot\Client($key, $secret);
$client->teamManagement()->updateUser($userId, ['first_name' => 'Bruno']);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
Map<String, Object> params = new HashMap<>();
params.put("first_name", "Bruno");
params.put("last_name", "Mars");
TestingbotTeamMember member = restApi.updateTeamMember(userId, params);
```

```javascript
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

```json
{
  "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` integer required:** Numeric ID of the team user whose credentials to rotate.

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

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

```csharp
var client = new TestingBotClient(key, secret);
var reset = await client.Team.ResetUserKeysAsync(userId);
```

```ruby
require 'testingbot'
api = TestingBot::Api.new(key, secret)
api.reset_credentials(user_id)
```

```python
import testingbotclient
tb = testingbotclient.TestingBotClient(key, secret)
tb.team.reset_keys(user_id)
```

```php
$client = new TestingBot\Client($key, $secret);
$credentials = $client->teamManagement()->resetUserKeys($userId);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
TestingbotTeamCredentialReset reset = restApi.resetTeamMemberKeys(userId);
```

```javascript
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

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

GET `/v1/team-management/service-accounts`

## List service accounts
 Returns the team's API-only service accounts, each with its own key and secret. Service accounts run tests on the grid and REST API but cannot sign in to the dashboard. Admin only. 
### Arguments

- **`offset` integer:** Skip this many service accounts from the start of the result set.
- **`count` integer:** Number of service accounts to return.

### Response fields

- **`data` array of service account objects:** Service accounts for this team.
- **`meta` meta object:** —

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

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

Response

```json
{
  "data": [
    {
      "id": 412,
      "identifier": "ci-runner",
      "key": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
      "secret": "f0e9d8c7b6a5948372615f4e3d2c1b0a",
      "created_at": "2026-06-05T10:08:35Z"
    }
  ],
  "meta": { "offset": 0, "count": 10, "total": 1 }
}
```

GET `/v1/team-management/service-accounts/:id`

## Get a service account
 Returns a single service account, including its key and secret. Admin only. 
### Arguments

- **`id` integer required:** Numeric ID of the service account.

### Response fields

- **`id` integer:** Unique numeric service-account ID.
- **`identifier` string:** Human-friendly label shown on the dashboard and test detail pages.
- **`key` string:** TestingBot API key (username) for this service account.
- **`secret` string:** TestingBot API secret for this service account. Store it as a CI secret.
- **`created_at` timestamp:** When the service account was created.

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

```bash
$ curl "https://api.testingbot.com/v1/team-management/service-accounts/:id" \
-u key:secret
```

Response

```json
{
  "id": 412,
  "identifier": "ci-runner",
  "key": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "secret": "f0e9d8c7b6a5948372615f4e3d2c1b0a",
  "created_at": "2026-06-05T10:08:35Z"
}
```

POST `/v1/team-management/service-accounts`

## Create a service account
 Creates a new API-only service account with its own generated key and secret. The identifier is a label (min 3 characters, unique within the team) shown next to the account's tests on the dashboard. Admin only. 
### Arguments

- **`identifier` string required min=`3 characters`:** Human-friendly label for the service account (min 3 characters, unique within the team).

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

```bash
$ curl -X POST -u key:secret \
  https://api.testingbot.com/v1/team-management/service-accounts \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "identifier": "ci-runner"
  }'
```

Response

```json
{
  "id": 412,
  "identifier": "ci-runner",
  "key": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "secret": "f0e9d8c7b6a5948372615f4e3d2c1b0a",
  "created_at": "2026-06-05T10:08:35Z"
}
```

PUT `/v1/team-management/service-accounts/:id`

## Update a service account
 Renames a service account by changing its identifier. The key and secret are unchanged. Admin only. 
### Arguments

- **`id` integer required:** Numeric ID of the service account to update.
- **`identifier` string required min=`3 characters`:** New identifier (min 3 characters, unique within the team).

### Response fields

- **`id` integer:** Unique numeric service-account ID.
- **`identifier` string:** Human-friendly label shown on the dashboard and test detail pages.
- **`key` string:** TestingBot API key (username) for this service account.
- **`secret` string:** TestingBot API secret for this service account. Store it as a CI secret.
- **`created_at` timestamp:** When the service account was created.

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

```bash
$ curl -X PUT -u key:secret \
  https://api.testingbot.com/v1/team-management/service-accounts/:id \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "identifier": "nightly-regression"
  }'
```

Response

```json
{
  "id": 412,
  "identifier": "nightly-regression",
  "key": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "secret": "f0e9d8c7b6a5948372615f4e3d2c1b0a",
  "created_at": "2026-06-05T10:08:35Z"
}
```

POST `/v1/team-management/service-accounts/:id/rotate`

## Rotate a service account access key
 Generates a fresh key and secret for the service account. The previous key and secret stop working immediately, so update any automation using them. Admin only. 
### Arguments

- **`id` integer required:** Numeric ID of the service account whose key and secret to rotate.

POST `/v1/team-management/service-accounts/:id/rotate`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X POST -u key:secret \
  https://api.testingbot.com/v1/team-management/service-accounts/:id/rotate
```

Response

```json
{
  "id": 412,
  "identifier": "ci-runner",
  "key": "9f8e7d6c5b4a3i2h1g0f9e8d7c6b5a40",
  "secret": "40a5b6c7d8e9f0g1h2i3a4b5c6d7e8f9",
  "created_at": "2026-06-05T10:08:35Z"
}
```

DELETE `/v1/team-management/service-accounts/:id`

## Delete a service account
 Permanently deletes the service account. Its key and secret stop working immediately. Tests it already ran stay in your history, still labelled with its identifier. Admin only. 
### Arguments

- **`id` integer required:** Numeric ID of the service account to delete.

DELETE `/v1/team-management/service-accounts/:id`
[cURL](https://testingbot.com#)
Request

```bash
$ curl -X DELETE -u key:secret \
  https://api.testingbot.com/v1/team-management/service-accounts/:id
```

Response

```json
{
  "success": true,
  "message": "Service account \"ci-runner\" deleted."
}
```

GET `/v1/team-management/users/{id}/client-key`

## Get a team user's client key
 Admin-only endpoint that returns the API client key for a specific team user. Used by tooling that impersonates team members in CI; the secret is not exposed here — use reset-keys to rotate. 
### Arguments

- **`id` integer required:** Numeric ID of the team user whose key to fetch.

### Response fields

- **`client_key` string:** The API client key. The matching secret is never returned here; rotate both with reset-keys.

GET `/v1/team-management/users/{id}/client-key`
[cURL](https://testingbot.com#) [.NET](https://testingbot.com#) [PHP](https://testingbot.com#) [Java](https://testingbot.com#) [NodeJS](https://testingbot.com#)
Request

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

```csharp
var client = new TestingBotClient(key, secret);
var clientKey = await client.Team.GetUserClientKeyAsync(userId);
```

```php
$client = new TestingBot\Client($key, $secret);
$clientKey = $client->teamManagement()->getUserClientKey($userId);
```

```java
TestingbotREST restApi = new TestingbotREST(key, secret);
JsonElement clientKey = restApi.getTeamMemberClientKey(userId);
```

```javascript
const TestingBot = require('testingbot-api');

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

const { client_key } = await api.getUserClientKey(userId);
```

[Previous Browsers & Devices](https://testingbot.com/support/api/devices) [Next Tests & Builds](https://testingbot.com/support/api/tests)
