Repository: saucelabs/node-saucelabs
Pull request #130 (2021-11-23 16:11:56)

Add builds-v2 API
### Changes

- Add builds-v2 API
- Add team-management API
- Remove builds-v1 API as it is deprecated
- Support existing commands to avoid breaking changes



Patch:
diff --git a/apis/builds.json b/apis/builds.json
new file mode 100644
index 0000000..3fea6cc
--- /dev/null
+++ b/apis/builds.json
@@ -0,0 +1,610 @@
+{
+  "swagger":"2.0",
+  "info":{
+    "title":"Builds API",
+    "version":"2.0.0",
+    "description":"Builds API"
+  },
+  "host":"saucelabs.com",
+  "servers":[
+    {
+      "url":"https://api.{region}.saucelabs.{tld}",
+      "variables":{
+        "region":{
+          "default":"us-west-1",
+          "description":"region of datacenter",
+          "enum":[
+            "us-west-1",
+            "us-east-1",
+            "eu-central-1",
+            "apac-southeast-1",
+            "staging"
+          ]
+        },
+        "tld":{
+          "default":"com",
+          "description":"internal or external API",
+          "enum":[
+            "net",
+            "com"
+          ]
+        }
+      }
+    }
+  ],
+  "basePath":"/v2/builds",
+  "schemes":[
+    "https"
+  ],
+  "produces":[
+    "application/json"
+  ],
+  "securityDefinitions":{
+    "BasicAuth":{
+      "type":"basic"
+    },
+    "PrivApiKey":{
+      "in":"header",
+      "name":"Signature",
+      "type":"apiKey"
+    }
+  },
+  "definitions":{
+    "JobsState":{
+      "properties":{
+        "completed":{
+          "type":"integer"
+        },
+        "errored":{
+          "type":"integer"
+        },
+        "failed":{
+          "type":"integer"
+        },
+        "finished":{
+          "type":"integer"
+        },
+        "passed":{
+          "type":"integer"
+        },
+        "public":{
+          "type":"integer"
+        },
+        "queued":{
+          "type":"integer"
+        },
+        "running":{
+          "type":"integer"
+        }
+      },
+      "type":"object"
+    },
+    "BuildV2":{
+      "description":"Build",
+      "properties":{
+        "creation_time":{
+          "type":"string"
+        },
+        "deletion_time":{
+          "type":"string"
+        },
+        "end_time":{
+          "type":"string"
+        },
+        "group_id":{
+          "type":"string"
+        },
+        "id":{
+          "type":"string"
+        },
+        "jobs": {
+          "$ref":"#/definitions/JobsState"
+        },
+        "modification_time":{
+          "type":"string"
+        },
+        "name":{
+          "type":"string"
+        },
+        "org_id":{
+          "type":"string"
+        },
+        "owner_id":{
+          "type":"string"
+        },
+        "passed":{
+          "type":"boolean"
+        },
+        "public":{
+          "type":"boolean"
+        },
+        "run":{
+          "type":"integer"
+        },
+        "start_time":{
+          "type":"integer"
+        },
+        "status":{
+          "type":"string"
+        },
+        "team_id":{
+          "type":"string"
+        }
+      },
+      "type":"object"
+    },
+    "BuildV2Job":{
+      "description":"Job",
+      "properties":{
+        "creation_time":{
+          "type":"string"
+        },
+        "deletion_time":{
+          "type":"string"
+        },
+        "id":{
+          "type":"string"
+        },
+        "modification_time":{
+          "type":"string"
+        },
+        "state": {
+          "$ref":"#/definitions/JobsState"
+        }
+      },
+      "type":"object"
+    }
+  },
+  "parameters":{
+    "buildOffset":{
+      "default":0,
+      "description":"Starting number",
+      "in":"query",
+      "name":"offset",
+      "type":"number",
+      "required": false
+    },
+    "buildLimit":{
+      "default":20,
+      "description":"Number of results to return",
+      "in":"query",
+      "name":"limit",
+      "type":"number",
+      "required": false
+    },
+    "buildUserId":{
+      "description":"user_id",
+      "in":"query",
+      "name":"user_id",
+      "type":"string",
+      "required": false
+    },
+    "buildOrgId":{
+      "description":"org_id",
+      "in":"query",
+      "name":"org_id",
+      "type":"string",
+      "required": false
+    },
+    "buildGroupId":{
+      "description":"group_id",
+      "in":"query",
+      "name":"group_id",
+      "type":"string",
+      "required": false
+    },
+    "buildTeamId":{
+      "description":"team_id",
+      "in":"query",
+      "name":"team_id",
+      "type":"string",
+      "required": false
+    },
+    "buildStatus":{
+      "enum":[
+        "running",
+        "error",
+        "failed",
+        "complete",
+        "success"
+      ],
+      "description":"status",
+      "in":"query",
+      "name":"status",
+      "type":"string",
+      "required": false
+    },
+    "buildStart":{
+      "description":"start",
+      "in":"query",
+      "name":"name",
+      "format":"date",
+      "type":"string",
+      "required": false
+    },
+    "buildEnd":{
+      "description":"end",
+      "in":"query",
+      "name":"end",
+      "format":"date",
+      "type":"string",
+      "required": false
+    },
+    "buildSort":{
+      "enum":[
+        "asc",
+        "desc"
+      ],
+      "description":"sort",
+      "in":"query",
+      "name":"sort",
+      "type":"string",
+      "required": false
+    },
+    "buildSource":{
+      "enum":[
+        "vdc",
+        "rdc"
+      ],
+      "description":"build_source",
+      "in":"path",
+      "name":"build_source",
+      "type":"string",
+      "required":true
+    },
+    "buildId":{
+      "description":"build_id",
+      "in":"path",
+      "name":"build_id",
+      "required":true,
+      "type":"string",
+      "required": false
+    },
+    "buildJobId":{
+      "description":"job_id",
+      "in":"path",
+      "name":"job_id",
+      "required":true,
+      "type":"string",
+      "required": false
+    },
+    "buildModifiedSince":{
+      "description":"modified_since",
+      "in":"query",
+      "name":"modified_since",
+      "type":"string",
+      "required": false
+    },
+    "buildCompleted":{
+      "description":"completed",
+      "in":"query",
+      "name":"completed",
+      "type":"boolean",
+      "required": false
+    },
+    "buildErrored":{
+      "description":"errored",
+      "in":"query",
+      "name":"errored",
+      "type":"string",
+      "required": false
+    },
+    "buildFailed":{
+      "description":"failed",
+      "in":"query",
+      "name":"failed",
+      "type":"string",
+      "required": false
+    },
+    "buildFinished":{
+      "description":"finished",
+      "in":"query",
+      "name":"finished",
+      "type":"string",
+      "required": false
+    },
+    "buildNew":{
+      "description":"new",
+      "in":"query",
+      "name":"new",
+      "type":"string",
+      "required": false
+    },
+    "buildPassed":{
+      "description":"passed",
+      "in":"query",
+      "name":"passed",
+      "type":"string",
+      "required": false
+    },
+    "buildPublic":{
+      "description":"public",
+      "in":"query",
+      "name":"public",
+      "type":"boolean",
+      "required": false
+    },
+    "buildQueued":{
+      "description":"queued",
+      "in":"query",
+      "name":"queued",
+      "type":"boolean",
+      "required": false
+    },
+    "buildRunning":{
+      "description":"running",
+      "in":"query",
+      "name":"running",
+      "type":"boolean",
+      "required": false
+    },
+    "buildFaulty":{
+      "description":"faulty",
+      "in":"query",
+      "name":"faulty",
+      "type":"boolean",
+      "required": false
+    }
+  },
+  "paths": {
+    "/{build_source}/":{
+      "get":{
+        "operationId":"get_builds_v2",
+        "parameters":[
+          {
+            "$ref":"#/parameters/buildSource"
+          },
+          {
+            "$ref":"#/parameters/buildUserId"
+          },
+          {
+            "$ref":"#/parameters/buildOrgId"
+          },
+          {
+            "$ref":"#/parameters/buildGroupId"
+          },
+          {
+            "$ref":"#/parameters/buildTeamId"
+          },
+          {
+            "$ref":"#/parameters/buildStatus"
+          },
+          {
+            "$ref":"#/parameters/buildStart"
+          },
+          {
+            "$ref":"#/parameters/buildEnd"
+          },
+          {
+            "$ref":"#/parameters/buildLimit"
+          },
+          {
+            "$ref":"#/parameters/buildOffset"
+          },
+          {
+            "$ref":"#/parameters/buildSort"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"List of Builds",
+            "schema":{
+              "properties":{
+                "builds":{
+                  "description":"list of builds",
+                  "items":{
+                    "$ref":"#/definitions/BuildV2"
+                  },
+                  "type":"array"
+                }
+              },
+              "type":"object"
+            }
+          },
+          "404":{
+            "description":"Not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get List of Builds",
+        "tags":[
+          "Build"
+        ]
+      }
+    },
+    "/{build_source}/{build_id}/":{
+      "get":{
+        "operationId":"get_build_v2",
+        "parameters":[
+          {
+            "$ref":"#/parameters/buildSource"
+          },
+          {
+            "$ref":"#/parameters/buildId"
+          },
+          {
+            "$ref":"#/parameters/buildUserId"
+          },
+          {
+            "$ref":"#/parameters/buildOrgId"
+          },
+          {
+            "$ref":"#/parameters/buildGroupId"
+          },
+          {
+            "$ref":"#/parameters/buildTeamId"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"Build",
+            "schema":{
+              "$ref":"#/definitions/BuildV2"
+            }
+          },
+          "404":{
+            "description":"Not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get Build detail",
+        "tags":[
+          "Build"
+        ]
+      }
+    },
+    "/{build_source}/jobs/{job_id}/build/":{
+      "get":{
+        "operationId":"get_build_by_jobId_v2",
+        "parameters":[
+          {
+            "$ref":"#/parameters/buildSource"
+          },
+          {
+            "$ref":"#/parameters/buildJobId"
+          },
+          {
+            "$ref":"#/parameters/buildUserId"
+          },
+          {
+            "$ref":"#/parameters/buildOrgId"
+          },
+          {
+            "$ref":"#/parameters/buildGroupId"
+          },
+          {
+            "$ref":"#/parameters/buildTeamId"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"Build",
+            "schema":{
+              "$ref":"#/definitions/BuildV2"
+            }
+          },
+          "404":{
+            "description":"Not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get Build Jobs",
+        "tags":[
+          "Build"
+        ]
+      }
+    },
+    "/{build_source}/{build_id}/jobs/":{
+      "get":{
+        "operationId":"get_builds_jobs_v2",
+        "parameters":[
+          {
+            "$ref":"#/parameters/buildSource"
+          },
+          {
+            "$ref":"#/parameters/buildId"
+          },
+          {
+            "$ref":"#/parameters/buildUserId"
+          },
+          {
+            "$ref":"#/parameters/buildOrgId"
+          },
+          {
+            "$ref":"#/parameters/buildGroupId"
+          },
+          {
+            "$ref":"#/parameters/buildTeamId"
+          },
+          {
+            "$ref":"#/parameters/buildModifiedSince"
+          },
+          {
+            "$ref":"#/parameters/buildCompleted"
+          },
+          {
+            "$ref":"#/parameters/buildErrored"
+          },
+          {
+            "$ref":"#/parameters/buildFailed"
+          },
+          {
+            "$ref":"#/parameters/buildFinished"
+          },
+          {
+            "$ref":"#/parameters/buildNew"
+          },
+          {
+            "$ref":"#/parameters/buildPassed"
+          },
+          {
+            "$ref":"#/parameters/buildPublic"
+          },
+          {
+            "$ref":"#/parameters/buildQueued"
+          },
+          {
+            "$ref":"#/parameters/buildRunning"
+          },
+          {
+            "$ref":"#/parameters/buildFaulty"
+          },
+          {
+            "$ref":"#/parameters/buildLimit"
+          },
+          {
+            "$ref":"#/parameters/buildOffset"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"Build Jobs",
+            "schema":{
+              "$ref":"#/definitions/BuildV2Job"
+            }
+          },
+          "404":{
+            "description":"Not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get Build detail",
+        "tags":[
+          "Build"
+        ]
+      }
+    }
+  }
+}
diff --git a/apis/sauce.json b/apis/sauce.json
index 474b371..2db2f52 100644
--- a/apis/sauce.json
+++ b/apis/sauce.json
@@ -943,6 +943,16 @@
       "name":"username",
       "required":true,
       "type":"string"
+    },
+    "jobIds":{
+      "description":"list of jobIds",
+      "in":"query",
+      "name":"id",
+      "required":true,
+      "items":{
+        "type":"string"
+      },
+      "type":"array"
     }
   },
   "paths":{
@@ -1063,49 +1073,6 @@
         ]
       }
     },
-    "/v1.1/{username}/builds":{
-      "get":{
-        "operationId":"list_builds",
-        "parameters":[
-          {
-            "$ref":"#/parameters/username"
-          },
-          {
-            "$ref":"#/parameters/limit"
-          },
-          {
-            "$ref":"#/parameters/subaccounts"
-          }
-        ],
-        "responses":{
-          "200":{
-            "description":"List of builds",
-            "schema":{
-              "items":{
-                "$ref":"#/definitions/Build"
-              },
-              "type":"array"
-            }
-          },
-          "404":{
-            "description":"User is not found",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          },
-          "default":{
-            "description":"Unexpected error",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          }
-        },
-        "summary":"Get all of a users builds",
-        "tags":[
-          "Build"
-        ]
-      }
-    },
     "/v1.1/{username}/jobs":{
       "get":{
         "operationId":"list_jobs",
@@ -1968,88 +1935,6 @@
         ]
       }
     },
-    "/v1/{username}/builds/{id}/failed-jobs":{
-      "get":{
-        "operationId":"list_build_failed_jobs",
-        "parameters":[
-          {
-            "$ref":"#/parameters/username"
-          },
-          {
-            "$ref":"#/parameters/id"
-          }
-        ],
-        "responses":{
-          "200":{
-            "description":"List of jobs",
-            "schema":{
-              "items":{
-                "$ref":"#/definitions/Job"
-              },
-              "type":"array"
-            }
-          },
-          "404":{
-            "description":"User is not found",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          },
-          "default":{
-            "description":"Unexpected error",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          }
-        },
-        "summary":"Get all of the jobs associated with a build that have failed",
-        "tags":[
-          "Build",
-          "Job"
-        ]
-      }
-    },
-    "/v1/builds/{id}/jobs":{
-      "get":{
-        "operationId":"list_build_jobs",
-        "parameters":[
-          {
-            "$ref":"#/parameters/id"
-          },
-          {
-            "$ref":"#/parameters/full"
-          }
-        ],
-        "responses":{
-          "200":{
-            "description":"List of jobs",
-            "schema":{
-              "items":{
-                "$ref":"#/definitions/Job"
-              },
-              "type":"array"
-            }
-          },
-          "404":{
-            "description":"User is not found",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          },
-          "default":{
-            "description":"Unexpected error",
-            "schema":{
-              "$ref":"#/definitions/Error"
-            }
-          }
-        },
-        "summary":"Get all of the jobs associated with a build",
-        "tags":[
-          "Build",
-          "Job"
-        ]
-      }
-    },
     "/v1/{username}/jobs/{id}":{
       "get":{
         "operationId":"get_job",
@@ -2348,6 +2233,50 @@
           "Asset"
         ]
       }
+    },
+    "/v1.1/jobs":{
+      "get":{
+        "operationId":"get_jobs_v1.1",
+        "stringifyOptions": {},
+        "parameters":[
+          {
+            "$ref":"#/parameters/jobIds"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"List of Jobs",
+            "schema":{
+              "properties":{
+                "jobs":{
+                  "description":"list of jobs",
+                  "items":{
+                    "$ref":"#/definitions/Job"
+                  },
+                  "type":"array"
+                }
+              },
+              "type":"object"
+            }
+          },
+          "404":{
+            "description":"User is not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get Job Information",
+        "tags":[
+          "Job"
+        ]
+      }
     }
   }
 }
diff --git a/apis/teamManagement.json b/apis/teamManagement.json
new file mode 100644
index 0000000..c0447b2
--- /dev/null
+++ b/apis/teamManagement.json
@@ -0,0 +1,339 @@
+{
+  "swagger":"2.0",
+  "info":{
+    "title":"TeamManagement API",
+    "version":"1.0.0",
+    "description":"TeamManagement API provides useful information about users and teams"
+  },
+  "host":"saucelabs.com",
+  "servers":[
+    {
+      "url":"https://api.{region}.saucelabs.{tld}",
+      "variables":{
+        "region":{
+          "default":"us-west-1",
+          "description":"region of datacenter",
+          "enum":[
+            "us-west-1",
+            "us-east-1",
+            "eu-central-1",
+            "apac-southeast-1",
+            "staging"
+          ]
+        },
+        "tld":{
+          "default":"com",
+          "description":"internal or external API",
+          "enum":[
+            "net",
+            "com"
+          ]
+        }
+      }
+    }
+  ],
+  "basePath":"/team-management/v1",
+  "schemes":[
+    "https"
+  ],
+  "produces":[
+    "application/json"
+  ],
+  "securityDefinitions":{
+    "BasicAuth":{
+      "type":"basic"
+    },
+    "PrivApiKey":{
+      "in":"header",
+      "name":"Signature",
+      "type":"apiKey"
+    }
+  },
+  "definitions":{
+    "TeamSettings":{
+      "properties":{
+        "virtual_machines":{
+          "type":"integer"
+        },
+        "real_devices":{
+          "type":"integer"
+        },
+        "live_only":{
+          "type":"boolean"
+        }
+      },
+      "type":"object"
+    },
+    "TeamGroup":{
+      "properties":{
+        "id":{
+          "type":"string"
+        },
+        "name":{
+          "type":"string"
+        }
+      },
+      "type":"object"
+    },
+    "Role":{
+      "properties":{
+        "role":{
+          "type":"integer"
+        },
+        "name":{
+          "type":"string"
+        }
+      },
+      "type":"object"
+    },
+    "OrganizationV1":{
+      "properties":{
+        "id":{
+          "type":"string"
+        },
+        "name":{
+          "type":"string"
+        }
+      },
+      "type":"object"
+    },
+    "UserV1":{
+      "description":"User",
+      "properties":{
+        "id":{
+          "type":"string"
+        },
+        "username":{
+          "type":"string"
+        },
+        "first_name":{
+          "type":"string"
+        },
+        "last_name":{
+          "type":"string"
+        },
+        "is_active":{
+          "type":"boolean"
+        },
+        "email":{
+          "type":"string"
+        },
+        "teams":{
+          "items":{
+            "$ref":"#/definitions/TeamV1"
+          },
+          "type":"array"
+        },
+        "roles":{
+          "items":{
+            "$ref":"#/definitions/Role"
+          },
+          "type":"array"
+        },
+        "organization":{
+          "$ref":"#/definitions/OrganizationV1"
+        }
+      },
+      "type":"object"
+    },
+    "TeamV1":{
+      "description":"Team",
+      "properties":{
+        "id":{
+          "type":"string"
+        },
+        "name":{
+          "type":"string"
+        },
+        "is_default":{
+          "type":"boolean"
+        },
+        "org_uuid":{
+          "type":"string"
+        },
+        "user_count":{
+          "type":"integer"
+        },
+        "settings":{
+          "$ref":"#/definitions/TeamSettings"
+        },
+        "group":{
+          "$ref":"#/definitions/TeamGroup"
+        }
+      },
+      "type":"object"
+    }
+  },
+  "parameters":{
+    "teamManagementOffset":{
+      "default":0,
+      "description":"Starting number",
+      "in":"query",
+      "name":"offset",
+      "type":"number",
+      "required": false
+    },
+    "teamManagementLimit":{
+      "default":20,
+      "description":"Number of results to return",
+      "in":"query",
+      "name":"limit",
+      "type":"number",
+      "required": false
+    },
+    "teamManagementUsername":{
+      "description":"username",
+      "in":"query",
+      "name":"username",
+      "type":"string",
+      "required": false
+    },
+    "teamManagementTeams":{
+      "description":"List of team_ids",
+      "in":"query",
+      "name":"teams",
+      "items":{
+        "type":"string"
+      },
+      "type":"array",
+      "required": false
+    },
+    "teamManagementTeamName":{
+      "description":"team-name",
+      "in":"query",
+      "name":"team-name",
+      "type":"string",
+      "required": false
+    },
+    "teamManagementRoles":{
+      "description":"roles",
+      "in":"query",
+      "name":"roles",
+      "type":"integer",
+      "required": false
+    },
+    "teamManagementPhrase":{
+      "description":"phrase",
+      "in":"query",
+      "name":"phrase",
+      "type":"string",
+      "required": false
+    },
+    "teamManagementStatus":{
+      "enum":[
+        "active",
+        "pending",
+        "inactive"
+      ],
+      "description":"status",
+      "in":"query",
+      "name":"status",
+      "type":"string",
+      "required": false
+    },
+    "teamManagementName":{
+      "description":"name",
+      "in":"query",
+      "name":"name",
+      "type":"string",
+      "required": false
+    }
+  },
+  "paths": {
+    "/teams":{
+      "get":{
+        "operationId":"get_teams_v1",
+        "parameters":[
+          {
+            "$ref":"#/parameters/teamManagementName"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"List of Teams",
+            "schema":{
+              "items":{
+                "$ref":"#/definitions/TeamV1"
+              },
+              "type":"array"
+            }
+          },
+          "404":{
+            "description":"Team is not found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get List of Teams",
+        "tags":[
+          "Team-Management"
+        ]
+      }
+    },
+    "/users":{
+      "get":{
+        "operationId":"get_users_v1",
+        "parameters":[
+          {
+            "$ref":"#/parameters/teamManagementUsername"
+          },
+          {
+            "$ref":"#/parameters/teamManagementTeams"
+          },
+          {
+            "$ref":"#/parameters/teamManagementTeamName"
+          },
+          {
+            "$ref":"#/parameters/teamManagementRoles"
+          },
+          {
+            "$ref":"#/parameters/teamManagementPhrase"
+          },
+          {
+            "$ref":"#/parameters/teamManagementStatus"
+          },
+          {
+            "$ref":"#/parameters/teamManagementLimit"
+          },
+          {
+            "$ref":"#/parameters/teamManagementOffset"
+          }
+        ],
+        "responses":{
+          "200":{
+            "description":"List of Users",
+            "schema":{
+              "items":{
+                "$ref":"#/definitions/UserV1"
+              },
+              "type":"array"
+            }
+          },
+          "404":{
+            "description":"Not Found",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          },
+          "default":{
+            "description":"Unexpected error",
+            "schema":{
+              "$ref":"#/definitions/Error"
+            }
+          }
+        },
+        "summary":"Get List of Teams",
+        "tags":[
+          "Team-Management"
+        ]
+      }
+    }
+  }
+}
diff --git a/package-lock.json b/package-lock.json
index 3373267..35c68da 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,7 @@
   "packages": {
     "": {
       "name": "saucelabs",
-      "version": "6.2.1",
+      "version": "7.0.4",
       "license": "Apache-2.0",
       "dependencies": {
         "bin-wrapper": "^4.1.0",
@@ -38,6 +38,7 @@
         "jest": "^27.3.0",
         "npm-run-all": "^4.1.5",
         "prettier": "^2.4.1",
+        "query-string": "^7.0.1",
         "release-it": "^14.11.6",
         "rimraf": "^3.0.2",
         "source-map-support": "^0.5.20",
@@ -11185,6 +11186,24 @@
         "query-string": "^6.13.8"
       }
     },
+    "node_modules/parse-path/node_modules/query-string": {
+      "version": "6.14.1",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+      "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
+      "dev": true,
+      "dependencies": {
+        "decode-uri-component": "^0.2.0",
+        "filter-obj": "^1.1.0",
+        "split-on-first": "^1.0.0",
+        "strict-uri-encode": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/parse-url": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-6.0.0.tgz",
@@ -11512,9 +11531,9 @@
       }
     },
     "node_modules/query-string": {
-      "version": "6.14.1",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
-      "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
+      "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
       "dev": true,
       "dependencies": {
         "decode-uri-component": "^0.2.0",
@@ -22395,6 +22414,20 @@
         "protocols": "^1.4.0",
         "qs": "^6.9.4",
         "query-string": "^6.13.8"
+      },
+      "dependencies": {
+        "query-string": {
+          "version": "6.14.1",
+          "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+          "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
+          "dev": true,
+          "requires": {
+            "decode-uri-component": "^0.2.0",
+            "filter-obj": "^1.1.0",
+            "split-on-first": "^1.0.0",
+            "strict-uri-encode": "^2.0.0"
+          }
+        }
       }
     },
     "parse-url": {
@@ -22647,9 +22680,9 @@
       }
     },
     "query-string": {
-      "version": "6.14.1",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
-      "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
+      "integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
       "dev": true,
       "requires": {
         "decode-uri-component": "^0.2.0",
diff --git a/package.json b/package.json
index b3a35a7..615a443 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
     "jest": "^27.3.0",
     "npm-run-all": "^4.1.5",
     "prettier": "^2.4.1",
+    "query-string": "^7.0.1",
     "release-it": "^14.11.6",
     "rimraf": "^3.0.2",
     "source-map-support": "^0.5.20",
diff --git a/src/constants.js b/src/constants.js
index b4c4fc3..596417a 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -19,7 +19,9 @@ const protocols = [
     require('../apis/performance.json'),
     require('../apis/testcomposer.json'),
     require('../apis/datastore.json'),
-    require('../apis/autonomiq.json')
+    require('../apis/autonomiq.json'),
+    require('../apis/teamManagement.json'),
+    require('../apis/builds.json')
 ]
 
 const protocolFlattened = new Map()
diff --git a/src/index.js b/src/index.js
index 2d96fab..2388b16 100644
--- a/src/index.js
+++ b/src/index.js
@@ -7,6 +7,8 @@ import got from 'got'
 import FormData from 'form-data'
 import BinWrapper from 'bin-wrapper'
 import { camelCase } from 'change-case'
+import { stringify } from 'query-string'
+
 
 import {
     createHMAC, getAPIHost, getAssetHost, toString, getParameters,
@@ -18,7 +20,8 @@ import {
     SC_PARAMS_TO_STRIP, SC_READY_MESSAGE, SC_CLOSE_MESSAGE,
     SC_CLOSE_TIMEOUT, DEFAULT_SAUCE_CONNECT_VERSION, SC_FAILURE_MESSAGES,
     SAUCE_CONNECT_VERSIONS_ENDPOINT, SC_WAIT_FOR_MESSAGES,
-    SC_BOOLEAN_CLI_PARAMS
+    SC_BOOLEAN_CLI_PARAMS,
+    USER_NOT_FOUND_MESSAGE
 } from './constants'
 
 export default class SauceLabs {
@@ -103,6 +106,22 @@ export default class SauceLabs {
             return ::this._uploadJobAssets
         }
 
+        if (propName === 'listBuilds') {
+            return ::this._listBuilds
+        }
+
+        if (propName === 'listBuildFailedJobs') {
+            return ::this._listBuildFailedJobs
+        }
+
+        if (propName === 'listBuildJobs') {
+            return ::this._listBuildJobs
+        }
+
+        if (propName === 'getUserByUsername') {
+            return ::this._getUserByUsername
+        }
+
         /**
          * allow to return publicly registered class properties
          */
@@ -128,83 +147,38 @@ export default class SauceLabs {
             return ::this._downloadJobAsset
         }
 
-        return async (...args) => {
-            const { description, method, endpoint, servers, basePath } = PROTOCOL_MAP.get(propName)
-            const params = getParameters(description.parameters)
-            const pathParams = params.filter(p => p.in === 'path')
-
-            /**
-             * validate required url params
-             */
-            let url = endpoint
-            for (const [i, urlParam] of Object.entries(pathParams)) {
-                const param = args[i]
-                const type = urlParam.type.replace('integer', 'number')
-
-                if (typeof param !== type) {
-                    throw new Error(`Expected parameter for url param '${urlParam.name}' from type '${type}', found '${typeof param}'`)
-                }
-
-                url = url.replace(`{${urlParam.name}}`, param)
-            }
-
-            /**
-             * check for body param (as last parameter as we don't expect request
-             * parameters for non idempotent requests)
-             */
-            let bodyOption = params.find(p => p.in === 'body') || description.requestBody
-                ? args[pathParams.length]
-                : null
-
-            if (bodyOption && typeof bodyOption === 'string') {
-                bodyOption = JSON.parse(bodyOption)
-            }
+        return this._callAPI.bind(this, propName)
+    }
 
-            /**
-             * validate required options
-             */
-            const bodyMap = new Map()
-            const options = args.slice(pathParams.length)[0] || {}
-            for (const optionParam of params.filter(p => p.in === 'query')) {
-                const expectedType = optionParam.type.replace('integer', 'number')
-                const optionName = camelCase(optionParam.name)
-                const option = options[optionName]
-                const isRequired = Boolean(optionParam.required) || (typeof optionParam.required === 'undefined' && typeof optionParam.default === 'undefined')
-                if ((isRequired || option) && !isValidType(option, expectedType)) {
-                    throw new Error(`Expected parameter for option '${optionName}' from type '${expectedType}', found '${typeof option}'`)
-                }
+    async _listBuilds(username, args) {
+        const { id: userId } = await this._getUserByUsername({ username })
+        const params = { userId, ...args }
+        const { builds } = await this._callAPI('getBuildsV2', 'vdc', params)
+        return builds;
+    }
 
-                if (typeof option !== 'undefined') {
-                    bodyMap.set(optionParam.name, option)
-                }
-            }
+    async _listBuildFailedJobs(username, buildId, args) {
+        const { id: userId } = await this._getUserByUsername({ username })
+        const params = { userId, faulty: true, ...args };
+        const { jobs: buildJobs } = await this._callAPI('getBuildsJobsV2', 'vdc', buildId, params)
+        const jobIds = buildJobs.map(({ id }) => id);
+        const { jobs } = await this._callAPI('getJobsV1_1', { id: jobIds, full: true })
+        return jobs;
+    }
 
-            /**
-             * get request body by using the body parameter or convert the parameter
-             * map into json object
-             */
-            const body = bodyOption || [...bodyMap.entries()].reduce((e, [k, v]) => {
-                e[k] = v
-                return e
-            }, {})
+    async _listBuildJobs(buildId, args) {
+        const { jobs: buildJobs } = await this._callAPI('getBuildsJobsV2', 'vdc', buildId, args)
+        const jobIds = buildJobs.map(({ id }) => id);
+        const { jobs } = await this._callAPI('getJobsV1_1', { ...args, id: jobIds, full: true })
+        return jobs;
+    }
 
-            /**
-             * make request
-             */
-            const uri = getAPIHost(servers, basePath, this._options) + url
-            try {
-                const response = await this._api[method](uri, {
-                    ...(
-                        method === 'get'
-                            ? { searchParams: body }
-                            : { json: body }
-                    ),
-                    responseType: 'json'
-                })
-                return response.body
-            } catch (err) {
-                throw new Error(`Failed calling ${propName}: ${err.message}, ${err.response && err.response.body}`)
-            }
+    async _getUserByUsername({ username }) {
+        try {
+            const { results: [user] } = await this._callAPI('getUsersV1', { username })
+            return user
+        } catch (err) {
+            throw new Error(`There was an error while fetching user information: ${err.message}`)
         }
     }
 
@@ -426,4 +400,89 @@ export default class SauceLabs {
             throw new Error(`There was an error uploading assets: ${err.message}`)
         }
     }
+
+    async _callAPI (propName, ...args) {
+        const { description, method, endpoint, servers, basePath } = PROTOCOL_MAP.get(propName)
+        const params = getParameters(description.parameters)
+        const pathParams = params.filter(p => p.in === 'path')
+
+        /**
+         * validate required url params
+         */
+        let url = endpoint
+        for (const [i, urlParam] of Object.entries(pathParams)) {
+            const param = args[i]
+            const type = urlParam.type.replace('integer', 'number')
+
+            if (typeof param !== type) {
+                throw new Error(`Expected parameter for url param '${urlParam.name}' from type '${type}', found '${typeof param}'`)
+            }
+
+            url = url.replace(`{${urlParam.name}}`, param)
+        }
+
+        /**
+         * check for body param (as last parameter as we don't expect request
+         * parameters for non idempotent requests)
+         */
+        let bodyOption = params.find(p => p.in === 'body') || description.requestBody
+            ? args[pathParams.length]
+            : null
+
+        if (bodyOption && typeof bodyOption === 'string') {
+            bodyOption = JSON.parse(bodyOption)
+        }
+
+        /**
+         * validate required options
+         */
+        const bodyMap = new Map()
+        const options = args.slice(pathParams.length)[0] || {}
+        for (const optionParam of params.filter(p => p.in === 'query')) {
+            const expectedType = optionParam.type.replace('integer', 'number')
+            const optionName = camelCase(optionParam.name)
+            const option = options[optionName]
+            const isRequired = Boolean(optionParam.required) || (typeof optionParam.required === 'undefined' && typeof optionParam.default === 'undefined')
+            if ((isRequired || option) && !isValidType(option, expectedType)) {
+                throw new Error(`Expected parameter for option '${optionName}' from type '${expectedType}', found '${typeof option}'`)
+            }
+
+            if (typeof option !== 'undefined') {
+                bodyMap.set(optionParam.name, option)
+            }
+        }
+
+        /**
+         * get request body by using the body parameter or convert the parameter
+         * map into json object
+         */
+        const body = bodyOption || [...bodyMap.entries()].reduce((e, [k, v]) => {
+            e[k] = v
+            return e
+        }, {})
+        /**
+         * stringify queryParams if stringifyOptions exists within description
+         */
+        const modifiedParams = description.stringifyOptions ?
+            stringify(body, description.stringifyOptions) :
+            body;
+            /**
+             * make request
+             */
+            const uri = getAPIHost(servers, basePath, this._options) + url
+        try {
+            const response = await this._api[method](uri, {
+                ...(
+                    method === 'get'
+                        ? { searchParams: modifiedParams }
+                        : { json: modifiedParams }
+                ),
+                responseType: 'json'
+            })
+            return response.body
+        } catch (err) {
+            throw new Error(`Failed calling ${propName}: ${err.message}, ${err.response && err.response.body}`)
+        }
+    }
+
 }


Test patch:
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 11b77b3..42df422 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -7,6 +7,128 @@ Object {
 }
 `;
 
+exports[`should get builds failed jobs 1`] = `
+Array [
+  Array [
+    "https://api.us-west-1.saucelabs.com/team-management/v1/users",
+    Object {
+      "responseType": "json",
+      "searchParams": Object {
+        "username": "fooUser",
+      },
+    },
+  ],
+  Array [
+    "https://api.us-west-1.saucelabs.com/v2/builds/vdc/build-1/jobs/",
+    Object {
+      "responseType": "json",
+      "searchParams": Object {
+        "faulty": true,
+        "limit": 10,
+        "offset": 5,
+        "user_id": "foo-id",
+      },
+    },
+  ],
+  Array [
+    "https://api.us-west-1.saucelabs.com/rest/v1.1/jobs",
+    Object {
+      "responseType": "json",
+      "searchParams": "id=job-1&id=job-2",
+    },
+  ],
+]
+`;
+
+exports[`should get builds failed jobs 2`] = `
+Array [
+  Object {
+    "id": "job-1",
+    "name": "foo-job",
+    "status": "failed",
+  },
+  Object {
+    "id": "job-2",
+    "name": "bar-job",
+    "status": "errored",
+  },
+]
+`;
+
+exports[`should get builds jobs 1`] = `
+Array [
+  Array [
+    "https://api.us-west-1.saucelabs.com/v2/builds/vdc/build-1/jobs/",
+    Object {
+      "responseType": "json",
+      "searchParams": Object {
+        "limit": 10,
+        "offset": 5,
+      },
+    },
+  ],
+  Array [
+    "https://api.us-west-1.saucelabs.com/rest/v1.1/jobs",
+    Object {
+      "responseType": "json",
+      "searchParams": "id=job-1&id=job-2",
+    },
+  ],
+]
+`;
+
+exports[`should get builds jobs 2`] = `
+Array [
+  Object {
+    "id": "job-1",
+    "name": "foo-job",
+    "status": "failed",
+  },
+  Object {
+    "id": "job-2",
+    "name": "bar-job",
+    "status": "errored",
+  },
+]
+`;
+
+exports[`should get list of builds 1`] = `
+Array [
+  Array [
+    "https://api.us-west-1.saucelabs.com/team-management/v1/users",
+    Object {
+      "responseType": "json",
+      "searchParams": Object {
+        "username": "fooUser",
+      },
+    },
+  ],
+  Array [
+    "https://api.us-west-1.saucelabs.com/v2/builds/vdc/",
+    Object {
+      "responseType": "json",
+      "searchParams": Object {
+        "limit": 10,
+        "offset": 5,
+        "user_id": "foo-id",
+      },
+    },
+  ],
+]
+`;
+
+exports[`should get user by username 1`] = `
+Array [
+  "https://api.us-west-1.saucelabs.com/team-management/v1/users",
+  Object {
+    "responseType": "json",
+    "searchParams": Object {
+      "username": "fooUser",
+    },
+  },
+]
+`;
+
 exports[`startSauceConnect should start sauce connect with fallback default version in case the call to the API failed 1`] = `
 Array [
   Array [
diff --git a/tests/cli.test.js b/tests/cli.test.js
index 28152df..faa69eb 100644
--- a/tests/cli.test.js
+++ b/tests/cli.test.js
@@ -6,7 +6,7 @@ jest.mock('../src/index.js', () => {
     class SauceLabsMock {
         constructor (options) {
             this.options = options
-            this.listBuilds = jest.fn().mockReturnValue(Promise.resolve(options.returnValue))
+            this.listJobs = jest.fn().mockReturnValue(Promise.resolve(options.returnValue))
         }
     }
 
@@ -26,10 +26,8 @@ test('should be able to execute a command', async () => {
     })
 
     const [command, description, handler, cb] = yargs.command.mock.calls[3]
-    expect(command).toContain('listBuilds <username>')
-    expect(command).toContain('[limit]')
-    expect(command).toContain('[subaccounts]')
-    expect(description).toBe('Get all of a users builds')
+    expect(command).toContain('listJobs <username> [limit] [subaccounts]')
+    expect(description).toBe('Get all of a users jobs')
 
     handler(yargs)
     expect(yargs.positional)
@@ -63,5 +61,5 @@ test('should be able to execute a command', async () => {
         headless: true,
         region: 'anywhere',
     })
-    expect(api.listBuilds).toBeCalledWith('username-param', params)
+    expect(api.listJobs).toBeCalledWith('username-param', params)
 })
diff --git a/tests/index.test.js b/tests/index.test.js
index 2c958c5..0c62f41 100644
--- a/tests/index.test.js
+++ b/tests/index.test.js
@@ -497,6 +497,72 @@ test('should output failure msg for createJob API', async () => {
     expect(error.message).toBe('Failed calling createJob: Response code 422 (Unprocessable Entity), empty framework')
 })
 
+test('should get user by username', async () => {
+    const api = new SauceLabs({ user: 'foo', key: 'bar' })
+    got.mockReturnValue(Promise.resolve({
+        body: { results: [{ id: 'foo-id' }]}
+    }))
+    const result = await api.getUserByUsername({ username: 'fooUser' })
+    expect(result).toEqual({ id: 'foo-id' })
+    expect(got.mock.calls[0]).toMatchSnapshot()
+})
+
+test('should get list of builds', async () => {
+    const api = new SauceLabs({ user: 'foo', key: 'bar' })
+    got.mockReturnValueOnce(Promise.resolve({
+        body: { results: [{ id: 'foo-id' }]}
+    })).mockReturnValue(Promise.resolve({
+        body: { builds: [{ id: 'build-id' }]}
+    }))
+    const builds = await api.listBuilds('fooUser', { offset: 5, limit: 10 })
+    expect(got.mock.calls).toMatchSnapshot()
+    expect(builds).toEqual([{ id: 'build-id' }])
+})
+
+test('should get builds failed jobs', async () => {
+    const api = new SauceLabs({ user: 'foo', key: 'bar' })
+    got.mockReturnValueOnce(Promise.resolve({
+        body: { results: [{ id: 'foo-id' }]}
+    })).mockReturnValueOnce(Promise.resolve({
+        body: { jobs: [{ id: 'job-1' }, { id: 'job-2' }]}
+    })).mockReturnValue(Promise.resolve({
+        body: { jobs: [
+            { id: 'job-1', name: 'foo-job', status: 'failed' },
+            { id: 'job-2', name: 'bar-job', status: 'errored' }
+        ]}
+    }))
+    const failedJobs = await api.listBuildFailedJobs('fooUser', 'build-1', { offset: 5, limit: 10 })
+    expect(got.mock.calls).toMatchSnapshot()
+    expect(failedJobs).toMatchSnapshot()
+})
+
+test('should get builds jobs', async () => {
+    const api = new SauceLabs({ user: 'foo', key: 'bar' })
+    got.mockReturnValueOnce(Promise.resolve({
+        body: { jobs: [{ id: 'job-1' }, { id: 'job-2' }]}
+    })).mockReturnValue(Promise.resolve({
+        body: { jobs: [
+            { id: 'job-1', name: 'foo-job', status: 'failed' },
+            { id: 'job-2', name: 'bar-job', status: 'errored' }
+        ]}
+    }))
+    const failedJobs = await api.listBuildJobs('build-1', { offset: 5, limit: 10 })
+    expect(got.mock.calls).toMatchSnapshot()
+    expect(failedJobs).toMatchSnapshot()
+})
+
+test('should stringify searchParams', () => {
+    const api = new SauceLabs({ user: 'foo', key: 'bar' })
+    got.mockReturnValue(Promise.resolve({
+        body: { jobs: [
+            { id: 'job-1', name: 'foo-job', status: 'failed' },
+            { id: 'job-2', name: 'bar-job', status: 'errored' }
+        ]}
+    }))
+    api.getJobsV1_1({ id: ['job-1', 'job-2'] })
+    expect(got.mock.calls[0][1].searchParams).toEqual("id=job-1&id=job-2")
+})
+
 afterEach(() => {
     fs.writeFileSync.mockClear()
     got.mockClear()