From e187c84d89a1ba8cbbc548e8f178c0760bede61d Mon Sep 17 00:00:00 2001 From: dobrac <4323173+dobrac@users.noreply.github.com> Date: Fri, 18 Sep 2026 15:27:57 +0000 Subject: [PATCH] chore: sync infra OpenAPI specs --- spec/openapi.dashboard-api.yaml | 782 +++- spec/openapi.infra.yaml | 4047 ++++++++++++----- .../shared/contracts/dashboard-api.types.ts | 902 +++- src/core/shared/contracts/infra-api.types.ts | 1942 ++++++-- 4 files changed, 5937 insertions(+), 1736 deletions(-) diff --git a/spec/openapi.dashboard-api.yaml b/spec/openapi.dashboard-api.yaml index 09bbedd64..7511c9461 100644 --- a/spec/openapi.dashboard-api.yaml +++ b/spec/openapi.dashboard-api.yaml @@ -103,6 +103,30 @@ components: schema: type: string format: uuid + clusterID: + name: clusterID + in: path + required: true + description: Identifier of the cluster. + schema: + type: string + format: uuid + projectID: + name: projectID + in: path + required: true + description: Identifier of the project. + schema: + type: string + format: uuid + userID: + name: userID + in: path + required: true + description: Identifier of the user. + schema: + type: string + format: uuid userId: name: userId in: path @@ -279,6 +303,12 @@ components: application/json: schema: $ref: "#/components/schemas/Error" + "412": + description: Legacy team mutations are disabled + content: + application/json: + schema: + $ref: "#/components/schemas/Error" "500": description: Server error content: @@ -400,6 +430,78 @@ components: format: email description: Billing/contact email for the team. + AdminClusterCreateRequest: + type: object + required: + - name + - endpoint + - endpoint_tls + - token + properties: + cluster_id: + type: string + format: uuid + description: Optional stable identifier for idempotent creation. Reuse succeeds only when the immutable configuration is identical. + name: + type: string + minLength: 1 + endpoint: + type: string + minLength: 1 + endpoint_tls: + type: boolean + token: + type: string + minLength: 1 + sandbox_proxy_domain: + type: string + nullable: true + auth_org_id: + type: string + nullable: true + + AdminClusterCreateResponse: + type: object + required: + - cluster_id + properties: + cluster_id: + type: string + format: uuid + + AdminTeamBlockRequest: + type: object + required: + - reason + properties: + reason: + type: string + minLength: 1 + maxLength: 1000 + description: Shown to the team as the reason it is blocked. + + AdminTeamClusterAssignmentRequest: + type: object + required: + - cluster_id + properties: + cluster_id: + type: string + format: uuid + preserve_existing: + type: boolean + default: false + description: Assign only when the team is unassigned or already assigned to cluster_id. + + AdminTeamClusterAssignmentResponse: + type: object + required: + - cluster_id + properties: + cluster_id: + type: string + format: uuid + BuildStatus: type: string description: Build status mapped for dashboard clients. @@ -636,22 +738,34 @@ components: format: int64 concurrentSandboxes: type: integer - format: int32 + format: int64 concurrentTemplateBuilds: type: integer - format: int32 + format: int64 maxVcpu: type: integer - format: int32 + format: int64 maxRamMb: type: integer - format: int32 + format: int64 diskMb: type: integer - format: int32 + format: int64 eventsTtlDays: type: integer - format: int32 + format: int64 + + TeamLimitsResponse: + type: object + required: + - tier + - limits + properties: + tier: + type: string + description: The team's raw tier identifier, exactly as stored (not normalized to a catalog plan). + limits: + $ref: "#/components/schemas/UserTeamLimits" UserTeam: type: object @@ -661,7 +775,6 @@ components: - slug - tier - email - - profilePictureUrl - isBlocked - isBanned - blockedReason @@ -680,9 +793,6 @@ components: type: string email: type: string - profilePictureUrl: - type: string - nullable: true isBlocked: type: boolean isBanned: @@ -708,6 +818,21 @@ components: items: $ref: "#/components/schemas/UserTeam" + TeamStatusResponse: + type: object + required: + - isBlocked + - isBanned + - blockedReason + properties: + isBlocked: + type: boolean + isBanned: + type: boolean + blockedReason: + type: string + nullable: true + TeamMember: type: object required: @@ -762,9 +887,6 @@ components: type: string minLength: 1 maxLength: 255 - profilePictureUrl: - type: string - nullable: true UpdateTeamResponse: type: object @@ -777,9 +899,6 @@ components: format: uuid name: type: string - profilePictureUrl: - type: string - nullable: true AddTeamMemberRequest: type: object @@ -1162,13 +1281,19 @@ components: slug: type: string - AdminControlPlaneProjectType: - type: string - enum: [development, staging, production] - - AdminControlPlaneProjectUpsertRequest: + ManagementProjectUpsertRequest: type: object - required: [name, slug, project_type] + description: >- + The properties of a project this side stores. Every one is synchronized + by the caller and sent on every push, so a reconcile is a complete + statement of the project rather than a patch. + + + A project's tier is not among them. It is assigned once, at creation, + from this side's own default, and no push moves it — limits arrive + separately and in full through upsertProjectLimits, which takes + precedence over the tier anyway. + required: [name, slug, email] properties: name: type: string @@ -1178,12 +1303,21 @@ components: type: string minLength: 1 maxLength: 63 - project_type: - $ref: "#/components/schemas/AdminControlPlaneProjectType" + description: >- + Changing it renames the project, and nothing follows it. Template + names embed the slug they were built under, so a renamed project + keeps its existing template names and only new ones carry the new + slug. A slug already held on this control plane is a 409, on a + rename as much as on a create. + email: + type: string + minLength: 1 + maxLength: 255 + description: Contact address recorded on the project. - AdminControlPlaneProject: + ManagementProject: allOf: - - $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + - $ref: "#/components/schemas/ManagementProjectUpsertRequest" - type: object required: [id] properties: @@ -1191,16 +1325,87 @@ components: type: string format: uuid - AdminControlPlaneMemberUpsertRequest: + ManagementClusterRegistrationRequest: type: object + required: + - name + - endpoint + - endpoint_tls + - token properties: - added_by: + name: type: string - format: uuid + minLength: 1 + endpoint: + type: string + minLength: 1 + endpoint_tls: + type: boolean + token: + type: string + minLength: 1 + sandbox_proxy_domain: + type: string + nullable: true + auth_org_id: + type: string + nullable: true + + ManagementProjectMemberIdentity: + type: object + required: [issuer, subject] + properties: + issuer: + type: string + minLength: 1 + maxLength: 2048 + subject: + type: string + minLength: 1 + maxLength: 2048 + + ManagementProjectMemberApplyRequest: + type: object + required: [revision, present] + properties: + revision: + type: integer + format: int64 + minimum: 1 + present: + type: boolean + is_default: + type: boolean + default: false + description: Whether this membership is the user's default team. Omitted requests from older sources remain non-default. + identities: + type: array + maxItems: 16 + items: + $ref: "#/components/schemas/ManagementProjectMemberIdentity" - AdminControlPlaneProjectLimits: + ManagementProjectLimits: type: object + description: >- + A project's effective limits, already resolved by the caller. Every + field is absolute: this side stores what it is given and performs no + arithmetic of its own. + + + The minimums below track the CHECK constraints on tiers, which is the + contract for what a limit may be. project_limits stores the same values + under looser constraints on purpose — it is a push target, and a floor + that only rejects the impossible keeps a future decision about what is + allowed a change to this schema rather than a migration. + + + `max_disk_size_mb` and `max_free_disk_size_mb` are one ceiling under two + names, and either name alone carries it. A caller that sends both must + send them equal; a caller that sends neither is refused. Sending both is + what a caller does while receivers older than the second name are still + running. required: + - revision - concurrent_sandboxes - max_sandbox_length_hours - max_vcpu @@ -1208,7 +1413,25 @@ components: - disk_mb - concurrent_template_builds - events_ttl_days + - default_free_disk_size_mb + anyOf: + - required: [max_free_disk_size_mb] + - required: [max_disk_size_mb] properties: + revision: + type: integer + format: int64 + minimum: 1 + description: >- + The caller's version of this answer, raised whenever the limits it + resolved for the project change. Delivery is over a network, so two + pushes can be in flight at once and arrive in either order: this + side stores the revision it accepted and drops a delivery at or + below it, which is what keeps a delayed retry from putting the + project back on limits it has already left. + + + Comparable only against earlier revisions for the same project. concurrent_sandboxes: type: integer format: int32 @@ -1237,14 +1460,38 @@ components: type: integer format: int32 minimum: 1 + default_free_disk_size_mb: + type: integer + format: int64 + minimum: 0 + description: >- + The default free-space growth target when a template build request + omits one. May sit anywhere at or below the maximum free-space + growth target, and usually sits well below it; a delivery whose + default exceeds the maximum is rejected. + max_free_disk_size_mb: + type: integer + format: int64 + minimum: 1 + description: >- + The most a template build may request as its free-space growth + target. + max_disk_size_mb: + type: integer + format: int64 + minimum: 1 + description: >- + The same ceiling as max_free_disk_size_mb, under the name it was + first published with. Kept until every deployed sender and receiver + speaks the other name. tags: - name: builds + - name: control-plane-management + description: Workspace control-plane operations authenticated with service JWTs. - name: sandboxes - name: teams - name: templates - - name: workspace-admin - description: Workspace control-plane admin operations authenticated with service JWTs. paths: /health: @@ -1405,6 +1652,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "412": + $ref: "#/components/responses/412" "500": $ref: "#/components/responses/500" @@ -1414,6 +1663,7 @@ paths: tags: [teams] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1431,6 +1681,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "412": + $ref: "#/components/responses/412" "500": $ref: "#/components/responses/500" @@ -1441,6 +1693,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1458,17 +1711,268 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "412": + $ref: "#/components/responses/412" "502": $ref: "#/components/responses/502" "500": $ref: "#/components/responses/500" + /admin/clusters: + post: + summary: Create a cluster + description: Creates a cluster whose configuration cannot be modified. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminClusterCreateRequest" + responses: + "201": + description: Cluster created. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminClusterCreateResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + + /admin/clusters/{clusterID}: + delete: + summary: Delete an unreferenced cluster + description: Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + responses: + "204": + description: Cluster deleted or already absent. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + + /v1/management/clusters/{clusterID}/destroy-readiness: + get: + operationId: managementClusterDestroyReadiness + summary: Check cluster destroy readiness + description: Checks whether this exact cluster has active templates or snapshots. Soft-deleted history and team assignments do not block this check. Returns success if the cluster is absent. This read does not change resources or prevent later template creation. + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/clusterID" + responses: + "204": + description: No active templates or snapshots reference the cluster. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "409": + description: Active templates or snapshots must be deleted before destroying the cluster. + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/cluster: + get: + summary: Get a team's assigned cluster + description: Returns the current cluster assignment without exposing cluster credentials. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "200": + description: Cluster assignment returned. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamClusterAssignmentResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + put: + summary: Assign a cluster to a team + description: >- + Updates the team's cluster reference to an existing cluster. New and replacement + assignments require a tier identifier containing `enterprise`, case-insensitively. + Replaying the identical assignment succeeds even if the team's tier later changes. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamClusterAssignmentRequest" + responses: + "204": + description: Cluster assigned. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "412": + $ref: "#/components/responses/412" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/cluster/{clusterID}: + delete: + summary: Detach a cluster from a team + description: Clears the assignment only when the team is unassigned or assigned to the specified cluster. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/clusterID" + responses: + "204": + description: Cluster detached or the team was already unassigned. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "412": + $ref: "#/components/responses/412" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/ban: + put: + summary: Ban a team + description: Marks the team as banned so its API keys stop authenticating. Idempotent; running workloads are not touched. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team banned. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + delete: + summary: Unban a team + description: Clears the team's ban. Idempotent. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team unbanned. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/block: + put: + summary: Block a team + description: Marks the team as blocked with the given reason, so it can no longer start sandboxes or builds. Idempotent; a repeated call replaces the reason. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamBlockRequest" + responses: + "204": + description: Team blocked. + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + delete: + summary: Unblock a team + description: Clears the team's block and its recorded reason. Idempotent. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "204": + description: Team unblocked. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + /admin/user-profiles/resolve: post: summary: Resolve user profiles tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1495,6 +1999,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] requestBody: required: true content: @@ -1521,6 +2026,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/userId" responses: @@ -1544,6 +2050,7 @@ paths: tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - $ref: "#/components/parameters/userId" responses: @@ -1557,6 +2064,8 @@ paths: $ref: "#/components/responses/404" "409": $ref: "#/components/responses/409" + "412": + $ref: "#/components/responses/412" "500": $ref: "#/components/responses/500" @@ -1613,6 +2122,60 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "412": + $ref: "#/components/responses/412" + "500": + $ref: "#/components/responses/500" + + /teams/{teamID}/status: + get: + summary: Get team access status + description: Returns whether the team is blocked or banned and its recorded blocked reason. Team-authenticated requests may read only the team they are scoped to. + tags: [teams] + security: + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "200": + description: Successfully returned team access status. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamStatusResponse" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + + /teams/{teamID}/limits: + get: + summary: Get team limits + description: Returns the team's tier and effective resource limits. Team-authenticated requests may read only the team they are scoped to. + tags: [teams] + security: + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + responses: + "200": + description: Successfully returned team limits. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamLimitsResponse" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" @@ -1661,6 +2224,8 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "412": + $ref: "#/components/responses/412" "404": $ref: "#/components/responses/404" "500": @@ -1685,6 +2250,8 @@ paths: $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "412": + $ref: "#/components/responses/412" "500": $ref: "#/components/responses/500" @@ -1889,13 +2456,13 @@ paths: "500": $ref: "#/components/responses/500" - /admin/v1/projects/{teamID}: + /v1/management/projects/{projectID}: parameters: - - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/projectID" put: - operationId: upsertProject - summary: Create or reconcile a project. - tags: [workspace-admin] + operationId: managementUpsertProject + summary: Create or reconcile a project (v1). + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: @@ -1903,20 +2470,20 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProjectUpsertRequest" + $ref: "#/components/schemas/ManagementProjectUpsertRequest" responses: "200": description: Existing project reconciled. content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProject" + $ref: "#/components/schemas/ManagementProject" "201": description: Project created. content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProject" + $ref: "#/components/schemas/ManagementProject" "400": $ref: "#/components/responses/400" "401": @@ -1928,9 +2495,15 @@ paths: "501": $ref: "#/components/responses/501" delete: - operationId: deleteProject - summary: Delete a project and its control-plane state. - tags: [workspace-admin] + operationId: managementDeleteProject + summary: Delete a project and its control-plane state (v1). + description: >- + Declared, and answered with 501 by every control plane. Deleting a + project means reclaiming templates, snapshots, volumes, running + sandboxes and their stored artifacts, and no single service can reach + all of them today. Callers should not depend on this operation until + that changes. + tags: [control-plane-management] security: - AdminJWTAuth: [] responses: @@ -1945,44 +2518,57 @@ paths: "501": $ref: "#/components/responses/501" - /admin/v1/projects/{teamID}/members/{userId}: + /v1/management/projects/{projectID}/members/{userID}: parameters: - - $ref: "#/components/parameters/teamID" - - $ref: "#/components/parameters/userId" + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/userID" put: - operationId: upsertProjectMember - summary: Reconcile an opaque user UUID as a project member. - tags: [workspace-admin] + operationId: managementApplyProjectMember + summary: Apply one versioned project member projection (v1). + description: >- + Applies the newest desired presence for one project member. An older + or duplicate revision is accepted without changing target state. + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: - required: false + required: true content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneMemberUpsertRequest" + $ref: "#/components/schemas/ManagementProjectMemberApplyRequest" responses: "204": - description: Membership is present. + description: Membership projection is applied or already superseded. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - "501": - $ref: "#/components/responses/501" - delete: - operationId: deleteProjectMember - summary: Remove a project member. - tags: [workspace-admin] + + /v1/management/projects/{projectID}/limits: + parameters: + - $ref: "#/components/parameters/projectID" + put: + operationId: managementUpsertProjectLimits + summary: Reconcile effective limits for a project (v1). + tags: [control-plane-management] security: - AdminJWTAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ManagementProjectLimits" responses: "204": - description: Membership is absent. + description: Effective limits are synchronized. "400": $ref: "#/components/responses/400" "401": @@ -1994,13 +2580,13 @@ paths: "501": $ref: "#/components/responses/501" - /admin/v1/projects/{teamID}/limits: + /v1/management/clusters/{clusterID}: parameters: - - $ref: "#/components/parameters/teamID" + - $ref: "#/components/parameters/clusterID" put: - operationId: upsertProjectLimits - summary: Reconcile effective limits for a project. - tags: [workspace-admin] + operationId: managementRegisterCluster + summary: Register a cluster (v1). + tags: [control-plane-management] security: - AdminJWTAuth: [] requestBody: @@ -2008,38 +2594,76 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/AdminControlPlaneProjectLimits" + $ref: "#/components/schemas/ManagementClusterRegistrationRequest" responses: "204": - description: Effective limits are synchronized. + description: Cluster registration is present. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + delete: + operationId: managementDeleteCluster + summary: Delete an unreferenced cluster (v1). + description: Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + responses: + "204": + description: Cluster is absent. + "401": + $ref: "#/components/responses/401" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - "501": - $ref: "#/components/responses/501" - /admin/v1/users/{userId}: + /v1/management/projects/{projectID}/cluster/{clusterID}: parameters: - - $ref: "#/components/parameters/userId" - delete: - operationId: purgeUser - summary: Purge shard-local membership and access-token state for an opaque user UUID. - tags: [workspace-admin] + - $ref: "#/components/parameters/projectID" + - $ref: "#/components/parameters/clusterID" + put: + operationId: managementAssignProjectCluster + summary: Assign a cluster to a project (v1). + description: >- + Assigns the cluster only when the project's tier identifier contains `enterprise`, + case-insensitively. Replaying the identical assignment succeeds even if the project's + tier later changes. + tags: [control-plane-management] security: - AdminJWTAuth: [] responses: "204": - description: User-owned shard state is absent. + description: Cluster is assigned to the project. "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "500": + $ref: "#/components/responses/500" + delete: + operationId: managementDetachProjectCluster + summary: Detach a cluster assignment from a project (v1). + tags: [control-plane-management] + security: + - AdminJWTAuth: [] + responses: + "204": + description: The matching assignment is absent. + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" "500": $ref: "#/components/responses/500" - "501": - $ref: "#/components/responses/501" diff --git a/spec/openapi.infra.yaml b/spec/openapi.infra.yaml index 4a26b3a42..d347ff14c 100644 --- a/spec/openapi.infra.yaml +++ b/spec/openapi.infra.yaml @@ -12,14 +12,6 @@ components: type: apiKey in: header name: X-API-Key - AccessTokenAuth: - type: http - scheme: bearer - bearerFormat: access_token - description: | - **Deprecated.** Access token authentication is deprecated and will be - removed in a future release. Use API key authentication (`X-API-Key`) - instead. # AuthProviderBearerAuth / AuthProviderTeamAuth: B before T in the name # so Bearer is validated before Team. AuthProviderBearerAuth: @@ -36,12 +28,31 @@ components: type: apiKey in: header name: X-Admin-Token + AdminJWTAuth: + type: http + scheme: bearer + bearerFormat: JWT AdminTeamAuth: type: apiKey in: header name: X-Team-ID parameters: + clusterID: + name: clusterID + in: path + required: true + schema: + type: string + format: uuid + description: Identifier of the cluster + rigID: + name: rigID + in: path + required: true + schema: + type: string + description: Rig identifier (e.g. "default") templateID: name: templateID in: path @@ -78,12 +89,6 @@ components: required: true schema: type: string - accessTokenID: - name: accessTokenID - in: path - required: true - schema: - type: string snapshotID: name: snapshotID in: path @@ -122,6 +127,35 @@ components: required: true schema: type: string + secretID: + name: secretID + in: path + required: true + schema: + type: string + description: > + Identifier of the secret (sec_ prefixed), or its canonical + lower-case name + + webhookID: + name: webhookID + in: path + required: true + schema: + type: string + format: uuid + headers: + XNextToken: + description: Cursor to fetch the next page of results, if more exist + schema: + type: string + XTotalRunning: + description: > + Number of running sandboxes matching the filters, before pagination is applied. + Only present when running sandboxes were requested. + schema: + type: integer + format: int32 responses: "400": @@ -154,8 +188,16 @@ components: application/json: schema: $ref: "#/components/schemas/Error" - "410": - description: Gone + "429": + description: Too many requests + headers: + Retry-After: + description: When present, the number of seconds to wait before retrying the request. + required: false + schema: + type: integer + minimum: 0 + example: 30 content: application/json: schema: @@ -166,8 +208,129 @@ components: application/json: schema: $ref: "#/components/schemas/Error" + "501": + description: Not implemented by this deployment + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "502": + description: Backend error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "503": + description: Service unavailable + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "504": + description: Backend timeout + content: + application/json: + schema: + $ref: "#/components/schemas/Error" schemas: + Rig: + description: An orchestrator node pool backed by one cloud scaling group + required: + - id + - provider + - resourceID + - capacityDesired + - capacityCurrent + properties: + id: + type: string + description: Rig identifier (e.g. "default") + provider: + type: string + description: Cloud provider backing the rig ("aws" or "gcp") + resourceID: + type: string + description: Canonical cloud resource ID of the scaling group backing the rig (ARN on AWS, self-link on GCP) + capacityDesired: + type: integer + format: int32 + description: Desired number of instances in the rig + capacityMin: + type: integer + format: int32 + description: > + Minimum capacity enforced on the rig's scaling group. Omitted when + nothing enforces bounds (GCP MIG without an active autoscaler). + capacityMax: + type: integer + format: int32 + description: > + Maximum capacity enforced on the rig's scaling group. Omitted when + nothing enforces bounds (GCP MIG without an active autoscaler). + capacityCurrent: + type: integer + format: int32 + description: Number of instances currently attached to the rig + + RigCapacityChange: + description: Desired capacity to set on the rig's scaling group + required: + - desired + properties: + desired: + type: integer + format: int32 + minimum: 0 + description: Absolute desired number of instances in the rig + + RigInstance: + description: An instance attached to a rig's scaling group + required: + - id + - transitioning + - terminating + properties: + id: + type: string + description: Provider instance ID (EC2 instance ID on AWS, instance name on GCP), also the node ID the orchestrator reports + createdAt: + type: string + format: date-time + description: When the provider created the instance. Omitted while the instance is transitioning. + transitioning: + type: boolean + description: The provider is creating, deleting, recreating or otherwise mutating the instance + terminating: + type: boolean + description: The instance is on its way out of the group and can never become healthy again + + RigError: + description: > + Scaling error on the rig's scaling group, e.g. a failed instance + creation due to resource exhaustion + required: + - timestamp + - code + - message + properties: + timestamp: + type: string + format: date-time + description: When the error occurred + code: + type: string + description: Provider-specific error code (e.g. ZONE_RESOURCE_POOL_EXHAUSTED, Failed) + message: + type: string + description: Human-readable error message + instance: + type: string + description: Instance the error relates to, if any + action: + type: string + description: Action being performed when the error occurred (e.g. CREATING) + Team: required: - teamID @@ -238,6 +401,17 @@ components: minimum: 0 description: Disk size for the sandbox in MiB + MinFreeDiskMb: + type: integer + format: int32 + minimum: 0 + description: >- + Requested minimum free space after the template's build steps, in MiB. + Omit to use the team's default. Set to 0 to request no minimum free-disk + growth. The filesystem is never shrunk, including inherited or already-larger + filesystems. Growth is best effort, so filesystem metadata can leave the + available space slightly below the requested minimum. + EnvdVersion: type: string description: Version of the envd running in the sandbox @@ -254,6 +428,14 @@ components: - running - paused + OrderDirection: + type: string + description: Sort direction + default: desc + enum: + - asc + - desc + SnapshotInfo: type: object required: @@ -302,12 +484,27 @@ components: maskRequestHost: type: string description: Specify host mask which will be used for all sandbox requests + httpsPorts: + type: array + description: Sandbox ports that serve HTTPS rather than plaintext HTTP. Affects how the proxy reaches the service inside the sandbox; the public URL is HTTPS either way. Certificates are not verified, so self-signed ones work. The envd port (49983) cannot be listed. + maxItems: 128 + uniqueItems: true + items: + type: integer + format: uint32 + minimum: 1 + maximum: 65535 rules: type: object description: > - Per-domain transform rules applied to matching egress HTTP/HTTPS requests. - Keys are domains (e.g. "api.example.com", "example.com"). - A domain listed here is not automatically allowed - use allowOut to permit the traffic. + Per-domain transform rules applied to matching outbound HTTPS requests. + Keys may be exact DNS names (for example, "api.example.com") or a leading wildcard + (for example, "*.example.com"), and are normalized to lowercase on write. + Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. + Exact rules take precedence, followed by the longest matching wildcard suffix, and + matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may + expose transformed credentials to every matching destination the sandbox contacts. + Rules do not grant network access; configure allowOut separately to permit the destination. additionalProperties: type: array items: @@ -331,7 +528,15 @@ components: $ref: "#/components/schemas/SandboxEgressProxyConfig" rules: type: object - description: Per-domain transform rules. Replaces all existing rules when provided. + description: > + Per-domain transform rules applied to matching outbound HTTPS requests. Replaces all + existing rules when provided. Keys may be exact DNS names or a single leading wildcard + (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match + subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take + precedence, followed by the longest matching wildcard suffix, and matching rule sets are + not merged. Broad wildcards such as "*.com" are allowed and may expose transformed + credentials to every matching destination the sandbox contacts. Rules do not grant + network access; configure allowOut separately to permit the destination. additionalProperties: type: array items: @@ -759,6 +964,59 @@ components: items: $ref: "#/components/schemas/SandboxVolumeMount" + NewSandboxV2: + description: >- + Sandbox creation request. All system communication with the sandbox is + always secured; the template's envd version must support secured access. + required: + - templateID + properties: + templateID: + type: string + description: Identifier of the required template + timeout: + type: integer + format: int32 + minimum: 1 + default: 300 + description: Time to live for the sandbox in seconds. + autoPause: + type: boolean + default: false + description: Automatically pauses the sandbox after the timeout + autoPauseMemory: + type: boolean + default: true + description: >- + Controls the snapshot kind taken when the sandbox auto-pauses on + timeout (only relevant when autoPause is true). When false, the + auto-pause drops the in-memory state and persists only the + filesystem (a filesystem-only snapshot); resuming it cold-boots + (reboots) the sandbox from disk. Such a snapshot cannot be + auto-resumed by traffic and must be resumed explicitly, so it cannot + be combined with autoResume. Defaults to true (full memory snapshot). + autoResume: + $ref: "#/components/schemas/SandboxAutoResumeConfig" + allow_internet_access: + type: boolean + description: + Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut + to 0.0.0.0/0 in the network config. + network: + $ref: "#/components/schemas/SandboxNetworkConfig" + metadata: + $ref: "#/components/schemas/SandboxMetadata" + envVars: + $ref: "#/components/schemas/EnvVars" + mcp: + $ref: "#/components/schemas/Mcp" + iam: + $ref: "#/components/schemas/SandboxIam" + volumeMounts: + type: array + items: + $ref: "#/components/schemas/SandboxVolumeMount" + SandboxIam: type: object description: >- @@ -800,6 +1058,15 @@ components: type: boolean deprecated: true description: Automatically pauses the sandbox after the timeout + memory: + type: boolean + description: >- + Defaults to true. When false, resume from disk state only: the sandbox cold-boots fresh and + any memory in the snapshot is ignored, never modified or deleted. Disk + state has crash-recovery semantics — writes not flushed before the pause + may be lost. A no-op for snapshots that contain no memory. Rejected with + an error in environments where this capability is not enabled, never + silently downgraded to a memory restore. ConnectSandbox: type: object @@ -811,6 +1078,34 @@ components: type: integer format: int32 minimum: 0 + memory: + type: boolean + description: >- + Defaults to true. When false and the sandbox is paused, resume from disk state only: the + sandbox cold-boots fresh and any memory in the snapshot is ignored, never + modified or deleted. Disk state has crash-recovery semantics — writes not + flushed before the pause may be lost. A no-op for snapshots that contain + no memory. Rejected with an error in environments where this capability + is not enabled, never silently downgraded to a memory restore. + + ConnectSandboxV2: + type: object + properties: + timeout: + description: Timeout in seconds from the current time after which the sandbox should expire + type: integer + format: int32 + minimum: 1 + default: 300 + memory: + type: boolean + description: >- + Defaults to true. When false and the sandbox is paused, resume from disk state only: the + sandbox cold-boots fresh and any memory in the snapshot is ignored, never + modified or deleted. Disk state has crash-recovery semantics — writes not + flushed before the pause may be lost. A no-op for snapshots that contain + no memory. Rejected with an error in environments where this capability + is not enabled, never silently downgraded to a memory restore. SandboxTimeoutRequest: type: object @@ -944,6 +1239,17 @@ components: type: integer description: Number of sandboxes that failed to kill + AdminTeamRunningSandboxCounts: + type: object + description: | + Cached live sandbox index count keyed by team ID. Counts may briefly + include sandboxes transitioning out of running; teams without indexed + sandboxes are omitted. + additionalProperties: + type: integer + format: int64 + minimum: 1 + AdminBuildCancelResult: required: - cancelledCount @@ -1074,71 +1380,6 @@ components: items: type: string - TemplateLegacy: - required: - - templateID - - buildID - - cpuCount - - memoryMB - - diskSizeMB - - public - - createdAt - - updatedAt - - createdBy - - lastSpawnedAt - - spawnCount - - buildCount - - envdVersion - - aliases - properties: - templateID: - type: string - description: Identifier of the template - buildID: - type: string - description: Identifier of the last successful build for given template - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" - diskSizeMB: - $ref: "#/components/schemas/DiskSizeMB" - public: - type: boolean - description: Whether the template is public or only accessible by the team - aliases: - type: array - description: Aliases of the template - items: - type: string - createdAt: - type: string - format: date-time - description: Time when the template was created - updatedAt: - type: string - format: date-time - description: Time when the template was last updated - createdBy: - allOf: - - $ref: "#/components/schemas/TeamUser" - nullable: true - lastSpawnedAt: - type: string - nullable: true - format: date-time - description: Time when the template was last used - spawnCount: - type: integer - format: int64 - description: Number of times the template was used - buildCount: - type: integer - format: int32 - description: Number of times the template was built - envdVersion: - $ref: "#/components/schemas/EnvdVersion" - TemplateBuild: required: - buildID @@ -1239,36 +1480,12 @@ components: type: boolean description: Whether the template is public or only accessible by the team - TemplateBuildRequest: + TemplateStep: + description: Step in the template build process required: - - dockerfile + - type properties: - alias: - description: Alias of the template - type: string - dockerfile: - description: Dockerfile for the template - type: string - teamID: - type: string - description: Identifier of the team - startCmd: - description: Start command to execute in the template after the build - type: string - readyCmd: - description: Ready check command to execute in the template after the build - type: string - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" - - TemplateStep: - description: Step in the template build process - required: - - type - properties: - type: + type: type: string description: Type of the step args: @@ -1309,22 +1526,8 @@ components: $ref: "#/components/schemas/CPUCount" memoryMB: $ref: "#/components/schemas/MemoryMB" - - TemplateBuildRequestV2: - required: - - alias - properties: - alias: - description: Alias of the template - type: string - teamID: - deprecated: true - type: string - description: Identifier of the team - cpuCount: - $ref: "#/components/schemas/CPUCount" - memoryMB: - $ref: "#/components/schemas/MemoryMB" + minFreeDiskMb: + $ref: "#/components/schemas/MinFreeDiskMb" FromImageRegistry: oneOf: @@ -1394,12 +1597,15 @@ components: TemplateBuildStartV2: type: object + description: Exactly one of fromImage or fromTemplate must be given and non-empty. properties: fromImage: type: string + minLength: 1 description: Image to use as a base for the template build fromTemplate: type: string + minLength: 1 description: Template to use as a base for the template build fromImageRegistry: $ref: "#/components/schemas/FromImageRegistry" @@ -1430,6 +1636,11 @@ components: url: description: Url where the file should be uploaded to type: string + headers: + description: Request headers that must be sent with the upload request + type: object + additionalProperties: + type: string LogLevel: type: string @@ -1495,7 +1706,8 @@ components: properties: logs: default: [] - description: Build logs + deprecated: true + description: Build logs (always empty since the V1 build path was removed, use logEntries) type: array items: type: string @@ -1551,7 +1763,6 @@ components: type: string description: | Status of the node. - - draining: the node is bound to be shut down. It will not accept new sandboxes and will stop once all existing sandboxes are done. - standby: the node is not actively used, but it can return to ready and continue serving traffic. enum: - ready @@ -1559,12 +1770,14 @@ components: - connecting - unhealthy - standby + - shutting_down x-enum-varnames: - NodeStatusReady - NodeStatusDraining - NodeStatusConnecting - NodeStatusUnhealthy - NodeStatusStandby + - NodeStatusShuttingDown NodeStatusChange: required: @@ -1691,6 +1904,7 @@ components: - status - statusChangedAt - sandboxCount + - outstandingWork - metrics - createSuccesses - createFails @@ -1726,6 +1940,15 @@ components: type: integer format: uint32 description: Number of sandboxes running on the node + maxSandboxes: + type: integer + format: int64 + description: Node-scoped configured sandbox admission limit. Nonpositive values reject creation. Omitted when unknown or not an orchestrator. + outstandingWork: + type: integer + format: uint64 + minimum: 0 + description: Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. metrics: $ref: "#/components/schemas/NodeMetrics" createSuccesses: @@ -1749,7 +1972,7 @@ components: - status - statusChangedAt - sandboxCount - - cachedBuilds + - outstandingWork - createSuccesses - createFails - version @@ -1784,13 +2007,17 @@ components: type: integer format: uint32 description: Number of sandboxes running on the node + maxSandboxes: + type: integer + format: int64 + description: Node-scoped configured sandbox admission limit. Nonpositive values reject creation. Omitted when unknown or not an orchestrator. + outstandingWork: + type: integer + format: uint64 + minimum: 0 + description: Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. metrics: $ref: "#/components/schemas/NodeMetrics" - cachedBuilds: - type: array - description: List of cached builds id on the node - items: - type: string createSuccesses: type: integer format: uint64 @@ -1800,39 +2027,6 @@ components: format: uint64 description: Number of sandbox create fails - CreatedAccessToken: - required: - - id - - name - - token - - mask - - createdAt - properties: - id: - type: string - format: uuid - description: Identifier of the access token - name: - type: string - description: Name of the access token - token: - type: string - description: The fully created access token - mask: - $ref: "#/components/schemas/IdentifierMaskingDetails" - createdAt: - type: string - format: date-time - description: Timestamp of access token creation - - NewAccessToken: - required: - - name - properties: - name: - type: string - description: Name of the access token - TeamAPIKey: required: - id @@ -1983,6 +2177,12 @@ components: type: integer format: int32 description: Error code + error_code: + type: string + description: >- + Machine-readable semantic error code. Not a closed set; initial values: + sandbox_capacity_unavailable, sandbox_placement_timeout, + sandbox_no_compatible_node, sandbox_create_failed, internal_server_error. message: type: string description: Error @@ -2032,6 +2232,13 @@ components: token: type: string description: Auth token to use for interacting with volume content + domain: + type: string + description: | + Domain to use as the destination for volume content requests, + replacing the default `api.`. Only returned when the + team is connected to a custom (BYOC) cluster; absent otherwise, in + which case the default domain is used. required: - volumeID - name @@ -2047,146 +2254,578 @@ components: required: - name -tags: - - name: templates - - name: sandboxes - - name: auth - - name: access-tokens - - name: api-keys - - name: tags - - name: volumes - -paths: - /health: - get: - summary: Health check - description: Health check - responses: - "204": - description: The service is healthy - "401": - $ref: "#/components/responses/401" + SecretMetadata: + type: object + description: > + Customer metadata of the secret. Always present, empty when unset. + At most 32 entries; keys are limited to 128 bytes, values to 1024 + bytes, and a secret's metadata to 8192 bytes in total. + maxProperties: 32 + additionalProperties: + type: string + maxLength: 1024 - /teams: - get: - summary: List teams - description: List all teams - tags: [auth] - security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - responses: - "200": - description: Successfully returned all teams - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Team" - "401": - $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" + Secret: + type: object + description: Metadata of a secret. It never carries the secret value. + required: + - secretID + - name + - currentVersion + - metadata + - createdAt + - updatedAt + properties: + secretID: + type: string + description: Identifier of the secret + name: + type: string + description: Name of the secret, unique within the project + currentVersion: + type: integer + format: int64 + description: Version served to readers that do not name one + metadata: + $ref: "#/components/schemas/SecretMetadata" + createdAt: + type: string + format: date-time + description: Time when the secret was created + updatedAt: + type: string + format: date-time + description: Time when the secret was last updated - /teams/{teamID}/metrics: - get: - summary: Team metrics - description: Get metrics for the team - tags: [auth] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/teamID" - - in: query - name: start - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the start of the interval, in seconds, for which the metrics - - in: query - name: end - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the end of the interval, in seconds, for which the metrics - responses: - "200": - description: Successfully returned the team metrics - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TeamMetric" - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" - "500": - $ref: "#/components/responses/500" + NewSecret: + type: object + required: + - name + - value + properties: + name: + type: string + minLength: 1 + maxLength: 128 + pattern: '^[a-zA-Z0-9_-]+$' + description: > + Name of the secret, unique within the project. Names are + lower-cased before storage and returned in that canonical form; + the sec_ prefix is reserved for secret identifiers. + value: + type: string + description: Runtime marker stored as the secret's first version. The runtime resolves it to a value at sandbox egress. + metadata: + $ref: "#/components/schemas/SecretMetadata" - /teams/{teamID}/metrics/max: - get: - summary: Maximum team metrics - description: Get the maximum metrics for the team in the given interval - tags: [auth] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/teamID" - - in: query - name: start - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the start of the interval, in seconds, for which the metrics - - in: query - name: end - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the end of the interval, in seconds, for which the metrics - - in: query - name: metric - required: true - schema: - type: string - enum: [concurrent_sandboxes, sandbox_start_rate] - description: Metric to retrieve the maximum value for - responses: - "200": - description: Successfully returned the team metrics - content: - application/json: - schema: - $ref: "#/components/schemas/MaxTeamMetric" - "400": - $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" - "500": - $ref: "#/components/responses/500" + SecretUpdate: + type: object + required: + - value + properties: + value: + type: string + description: Runtime marker stored as the secret's new version. The runtime resolves it to a value at sandbox egress. + metadata: + $ref: "#/components/schemas/SecretMetadata" - /sandboxes: - get: + SandboxEvent: + description: Sandbox event + required: + - id + - version + - type + - timestamp + - sandboxId + - sandboxExecutionId + - sandboxTemplateId + - sandboxBuildId + - sandboxTeamId + properties: + id: + type: string + format: uuid + description: Event unique identifier + version: + type: string + description: Event structure version + type: + type: string + description: Event name + eventCategory: + type: string + deprecated: true + description: Category of the event (e.g., 'lifecycle', 'process', etc.) + eventLabel: + type: string + deprecated: true + description: Label for the specific event type (e.g., 'sandbox_started', 'process_oom', etc.) + eventData: + type: object + nullable: true + description: Optional JSON data associated with the event + + timestamp: + type: string + format: date-time + description: Timestamp of the event + sandboxId: + type: string + format: string + description: Unique identifier for the sandbox + sandboxExecutionId: + type: string + format: string + description: Unique identifier for the sandbox execution + sandboxTemplateId: + type: string + format: string + description: Unique identifier for the sandbox template + sandboxBuildId: + type: string + format: string + description: Unique identifier for the sandbox build + sandboxTeamId: + type: string + format: uuid + description: Team identifier associated with the sandbox + WebhookCreate: + description: Configuration for registering new webhooks + required: + - name + - url + - events + - signatureSecret + properties: + name: + type: string + url: + type: string + format: uri + events: + type: array + items: + type: string + enabled: + type: boolean + default: true + signatureSecret: + type: string + description: Secret used to sign the webhook payloads + WebhookCreation: + description: Webhook creation response + required: + - id + - name + - createdAt + - teamId + - url + - enabled + - events + properties: + id: + type: string + description: Webhook unique identifier + name: + type: string + description: Webhook user friendly name + createdAt: + type: string + format: date-time + description: Time when the template was created + teamId: + type: string + description: Unique identifier for the team + url: + type: string + format: uri + enabled: + type: boolean + events: + type: array + items: + type: string + WebhookDetail: + description: Webhook detail response + required: + - id + - teamId + - name + - createdAt + - url + - enabled + - events + properties: + id: + type: string + description: Webhook unique identifier + teamId: + type: string + description: Unique identifier for the team + name: + type: string + description: Webhook user friendly name + createdAt: + type: string + format: date-time + description: Time when the template was created + url: + type: string + format: uri + enabled: + type: boolean + events: + type: array + items: + type: string + WebhookConfiguration: + description: Configuration for updating existing webhooks + properties: + enabled: + type: boolean + name: + type: string + description: Webhook user friendly name + url: + type: string + format: uri + events: + type: array + items: + type: string + signatureSecret: + type: string + description: Secret used to sign the webhook payloads + WebhookDelivery: + description: Webhook delivery attempt + required: + - id + - teamId + - webhookId + - eventId + - sandboxId + - eventType + - status + - durationMs + - requestBody + - requestHeaders + - requestUrl + - errorClass + - timestamp + properties: + id: + type: string + format: uuid + description: Delivery attempt identifier + teamId: + type: string + format: uuid + description: Team identifier + webhookId: + type: string + format: uuid + description: Webhook configuration identifier + eventId: + type: string + format: uuid + description: Sandbox event identifier + sandboxId: + type: string + description: Sandbox identifier + eventType: + type: string + description: Sandbox event type + status: + type: string + enum: [success, failed] + description: Delivery attempt status + durationMs: + type: integer + format: int32 + description: Delivery request duration in milliseconds + requestBody: + type: string + description: Serialized webhook request body + requestHeaders: + type: string + description: JSON-encoded request headers with sensitive values redacted + requestUrl: + type: string + format: uri + description: URL attempted for this delivery + responseBody: + type: string + nullable: true + description: Truncated response body, if a response was received + responseHeaders: + type: string + nullable: true + description: JSON-encoded response headers, if a response was received + responseHttpStatusCode: + type: integer + format: int32 + nullable: true + description: HTTP response status code, if a response was received + errorClass: + type: string + nullable: true + enum: + - http_error + - dns_error + - timeout + - transport_error + - request_error + - signature_error + - canceled + description: Machine-readable non-HTTP or HTTP failure class + errorMessage: + type: string + nullable: true + description: Error message for failures without a useful response body + timestamp: + type: string + format: date-time + description: Time when the delivery attempt started + WebhookDeliveryStats: + description: Webhook delivery aggregate stats + required: + - buckets + - total + - failed + - durationMs + properties: + buckets: + type: array + items: + $ref: "#/components/schemas/WebhookDeliveryStatsBucket" + total: + type: integer + format: int64 + failed: + type: integer + format: int64 + durationMs: + $ref: "#/components/schemas/WebhookDeliveryDurationStats" + WebhookDeliveryDurationStats: + description: Webhook delivery duration statistics in milliseconds + required: + - minimum + - average + - maximum + properties: + minimum: + type: number + format: double + average: + type: number + format: double + maximum: + type: number + format: double + WebhookDeliveryStatsBucket: + description: Webhook delivery stats for a time bucket + required: + - timestamp + - total + - failed + - durationMs + properties: + timestamp: + type: string + format: date-time + total: + type: integer + format: int64 + failed: + type: integer + format: int64 + durationMs: + $ref: "#/components/schemas/WebhookDeliveryDurationStats" + WebhookDeliveryGroup: + description: Webhook delivery attempts grouped by sandbox event + required: + - eventId + - eventType + - sandboxId + - attempts + properties: + eventId: + type: string + format: uuid + eventType: + type: string + sandboxId: + type: string + attempts: + type: array + items: + $ref: "#/components/schemas/WebhookDelivery" + WebhookDeliveriesListPayload: + description: Paginated webhook delivery attempts grouped by event + required: + - data + - nextCursor + properties: + data: + type: array + items: + $ref: "#/components/schemas/WebhookDeliveryGroup" + nextCursor: + type: string + nullable: true + description: Cursor to pass to the next list request, or null when there is no next page. +tags: + - name: templates + - name: sandboxes + - name: auth + - name: api-keys + - name: tags + - name: volumes + - name: secrets + +paths: + /health: + get: + summary: Health check + description: Health check + responses: + "204": + description: The service is healthy + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + + /teams: + get: + summary: List teams + description: List all teams + tags: [auth] + security: + - AuthProviderBearerAuth: [] + responses: + "200": + description: Successfully returned all teams + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Team" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /teams/{teamID}/metrics: + get: + summary: Team metrics + description: Get metrics for the team + tags: [auth] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + - in: query + name: start + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the start of the interval, in seconds, for which the metrics + - in: query + name: end + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the end of the interval, in seconds, for which the metrics + responses: + "200": + description: Successfully returned the team metrics + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TeamMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /teams/{teamID}/metrics/max: + get: + summary: Maximum team metrics + description: Get the maximum metrics for the team in the given interval + tags: [auth] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/teamID" + - in: query + name: start + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the start of the interval, in seconds, for which the metrics + - in: query + name: end + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the end of the interval, in seconds, for which the metrics + - in: query + name: metric + required: true + schema: + type: string + enum: [concurrent_sandboxes, sandbox_start_rate] + description: Metric to retrieve the maximum value for + responses: + "200": + description: Successfully returned the team metrics + content: + application/json: + schema: + $ref: "#/components/schemas/MaxTeamMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes: + get: summary: List running sandboxes + x-api-group: list description: List all running sandboxes. Use GET /v2/sandboxes instead. deprecated: true tags: [sandboxes] @@ -2196,31 +2835,637 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: metadata + in: query + description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. + required: false + schema: + type: string + responses: + "200": + description: Successfully returned all running sandboxes + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListedSandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + post: + summary: Create sandbox + description: Create a sandbox from the template. Use POST /v2/sandboxes instead. + deprecated: true + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSandbox" + responses: + "201": + description: The sandbox was created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + + /v2/sandboxes: + post: + summary: Create sandbox (v2) + description: Create a sandbox from the template. All system communication with the sandbox is secured. + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/NewSandboxV2" + responses: + "201": + description: The sandbox was created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + get: + summary: List sandboxes (v2) + x-api-group: list + description: List all sandboxes + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: metadata + in: query + description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. + required: false + schema: + type: string + - name: state + in: query + description: Filter sandboxes by one or more states + required: false + schema: + type: array + items: + $ref: "#/components/schemas/SandboxState" + style: form + explode: false + - name: order + in: query + description: Sort direction by sandbox start time. Defaults to desc (newest first). + required: false + schema: + $ref: "#/components/schemas/OrderDirection" + - name: startedAfter + in: query + description: Return sandboxes started at or after this timestamp. + required: false + schema: + type: string + format: date-time + - name: template + in: query + description: Filter sandboxes by a template ID or alias. + required: false + schema: + type: string + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" + responses: + "200": + description: Successfully returned all running sandboxes + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" + X-Total-Running: + $ref: "#/components/headers/XTotalRunning" + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ListedSandbox" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/metrics: + get: + summary: List sandbox metrics + x-api-group: list + description: List metrics for given sandboxes + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: sandbox_ids + in: query + required: true + description: Comma-separated list of sandbox IDs to get metrics for + explode: false + schema: + type: array + items: + type: string + maxItems: 100 + uniqueItems: true + responses: + "200": + description: Successfully returned all running sandboxes with metrics + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxesWithMetrics" + "401": + $ref: "#/components/responses/401" + "400": + $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/{sandboxID}/logs: + get: + summary: Sandbox logs + description: Get sandbox logs. Use /v2/sandboxes/{sandboxID}/logs instead. + deprecated: true + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + - in: query + name: start + schema: + type: integer + format: int64 + minimum: 0 + description: Starting timestamp of the logs that should be returned in milliseconds + - in: query + name: limit + schema: + default: 1000 + format: int32 + minimum: 0 + type: integer + description: Maximum number of logs that should be returned + responses: + "200": + description: Successfully returned the sandbox logs + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxLogs" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /v2/sandboxes/{sandboxID}/logs: + get: + summary: Sandbox logs (v2) + description: Get sandbox logs + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + - in: query + name: cursor + schema: + type: integer + format: int64 + minimum: 0 + description: Starting timestamp of the logs that should be returned in milliseconds + - in: query + name: limit + schema: + default: 1000 + type: integer + format: int32 + minimum: 0 + maximum: 1000 + description: Maximum number of logs that should be returned + - in: query + name: direction + schema: + $ref: "#/components/schemas/LogsDirection" + description: Direction of the logs that should be returned + - in: query + name: level + schema: + $ref: "#/components/schemas/LogLevel" + description: Minimum log level to return. Logs below this level are excluded + - in: query + name: search + schema: + type: string + maxLength: 256 + description: Case-sensitive substring match on log message content + responses: + "200": + description: Successfully returned the sandbox logs + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxLogsV2Response" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/{sandboxID}: + get: + summary: Sandbox + description: Get a sandbox by id + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + responses: + "200": + description: Successfully returned the sandbox + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxDetail" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + delete: + summary: Kill sandbox + description: Kill a sandbox + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + responses: + "204": + description: The sandbox was killed successfully + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/{sandboxID}/metrics: + get: + summary: Sandbox metrics + description: Get sandbox metrics + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + - in: query + name: start + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the start of the interval, in seconds, for which the metrics + - in: query + name: end + schema: + type: integer + format: int64 + minimum: 0 + description: Unix timestamp for the end of the interval, in seconds, for which the metrics + + responses: + "200": + description: Successfully returned the sandbox metrics + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxMetric" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + # TODO: Pause and resume might be exposed as POST /sandboxes/{sandboxID}/snapshot and then POST /sandboxes with specified snapshotting setup + /sandboxes/{sandboxID}/pause: + post: + summary: Pause sandbox + description: Pause the sandbox + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxPauseRequest" + responses: + "204": + description: The sandbox was paused successfully and can be resumed + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + + /sandboxes/{sandboxID}/resume: + post: + summary: Resume sandbox + deprecated: true + description: Resume the sandbox + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/ResumedSandbox" + responses: + "201": + description: The sandbox was resumed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + + /sandboxes/{sandboxID}/fork: + post: + summary: Fork sandbox + description: >- + Fork the sandbox: checkpoint the running sandbox in place (it is + briefly paused, snapshotted with its full memory state, and resumed on + its node, keeping its ID and expiration untouched) and create count + new sandboxes from that snapshot. Returns one result per requested + fork, each carrying either the created sandbox or the error that + prevented it from starting. A non-201 status means the request failed + before any fork was attempted. + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: false + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxForkRequest" + responses: + "201": + description: >- + The sandbox was snapshotted and the forks were attempted; each + entry reports one fork's outcome + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxForkResult" + "409": + $ref: "#/components/responses/409" + "404": + $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + + /sandboxes/{sandboxID}/connect: + post: + summary: Connect sandbox + description: Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. Use POST /v2/sandboxes/{sandboxID}/connect instead. + deprecated: true + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: metadata - in: query - description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. - required: false - schema: - type: string + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectSandbox" responses: "200": - description: Successfully returned all running sandboxes + description: The sandbox was already running content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/ListedSandbox" - "401": - $ref: "#/components/responses/401" + $ref: "#/components/schemas/Sandbox" + "201": + description: The sandbox was resumed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" "400": $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + + /v2/sandboxes/{sandboxID}/connect: post: - summary: Create sandbox - description: Create a sandbox from the template + summary: Connect sandbox (v2) + description: >- + Returns sandbox details. If the sandbox is paused, it will be resumed. + TTL is only extended. The request body is optional; an omitted timeout + defaults to 300 seconds. tags: [sandboxes] security: - ApiKeyAuth: [] @@ -2228,625 +3473,777 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" requestBody: - required: true + required: false content: application/json: schema: - $ref: "#/components/schemas/NewSandbox" + $ref: "#/components/schemas/ConnectSandboxV2" responses: + "200": + description: The sandbox was already running + content: + application/json: + schema: + $ref: "#/components/schemas/Sandbox" "201": - description: The sandbox was created successfully + description: The sandbox was resumed successfully content: application/json: schema: $ref: "#/components/schemas/Sandbox" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + "503": + $ref: "#/components/responses/503" + "504": + $ref: "#/components/responses/504" + + /sandboxes/{sandboxID}/timeout: + post: + summary: Set sandbox timeout + description: Set the timeout for the sandbox. The sandbox will expire x seconds from the time of the request. Calling this method multiple times overwrites the TTL, each time using the current timestamp as the starting point to measure the timeout duration. + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + tags: [sandboxes] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxTimeoutRequest" + parameters: + - $ref: "#/components/parameters/sandboxID" + responses: + "204": + description: Successfully set the sandbox timeout + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/{sandboxID}/network: + put: + summary: Update sandbox network + description: Update the network configuration for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting field clears it. + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + tags: [sandboxes] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxNetworkUpdateConfig" + parameters: + - $ref: "#/components/parameters/sandboxID" + responses: + "204": + description: Successfully updated the sandbox network configuration + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /sandboxes/{sandboxID}/refreshes: + post: + summary: Refresh sandbox + description: Refresh the sandbox extending its time to live + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + tags: [sandboxes] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxRefreshRequest" + parameters: + - $ref: "#/components/parameters/sandboxID" + responses: + "204": + description: Successfully refreshed the sandbox "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + + /sandboxes/{sandboxID}/snapshots: + post: + summary: Create snapshot + description: Create a persistent snapshot from the sandbox's current state. Snapshots can be used to create new sandboxes and persist beyond the original sandbox's lifetime. + tags: [sandboxes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/sandboxID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SandboxSnapshotRequest" + responses: + "201": + description: Snapshot created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/SnapshotInfo" "400": $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v2/sandboxes: + /snapshots: get: - summary: List sandboxes (v2) - description: List all sandboxes - tags: [sandboxes] + summary: List snapshots + x-api-group: list + description: List all snapshots for the team + tags: [snapshots] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: metadata + - name: sandboxID in: query - description: Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. required: false schema: type: string - - name: state + description: Filter snapshots by source sandbox ID + - name: name in: query - description: Filter sandboxes by one or more states + description: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-team/my-snapshot" or "my-snapshot:v1"). required: false schema: - type: array - items: - $ref: "#/components/schemas/SandboxState" - style: form - explode: false - - $ref: "#/components/parameters/paginationNextToken" + type: string - $ref: "#/components/parameters/paginationLimit" + - $ref: "#/components/parameters/paginationNextToken" responses: "200": - description: Successfully returned all running sandboxes + description: Successfully returned snapshots + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: type: array items: - $ref: "#/components/schemas/ListedSandbox" + $ref: "#/components/schemas/SnapshotInfo" "401": $ref: "#/components/responses/401" - "400": - $ref: "#/components/responses/400" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/metrics: - get: - summary: List sandbox metrics - description: List metrics for given sandboxes - tags: [sandboxes] + /v3/templates: + post: + summary: Create template (v3) + description: Create a new template + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - name: sandbox_ids - in: query - required: true - description: Comma-separated list of sandbox IDs to get metrics for - explode: false - schema: - type: array - items: - type: string - maxItems: 100 - uniqueItems: true + - AdminJWTAuth: [] + AdminTeamAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TemplateBuildRequestV3" + responses: - "200": - description: Successfully returned all running sandboxes with metrics + "202": + description: The build was requested successfully content: application/json: schema: - $ref: "#/components/schemas/SandboxesWithMetrics" - "401": - $ref: "#/components/responses/401" + $ref: "#/components/schemas/TemplateRequestResponseV3" "400": $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/logs: + /v2/templates: get: - summary: Sandbox logs - description: Get sandbox logs. Use /v2/sandboxes/{sandboxID}/logs instead. - deprecated: true - tags: [sandboxes] + summary: List templates (v2) + x-api-group: list + description: List all templates + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" - - in: query - name: start - schema: - type: integer - format: int64 - minimum: 0 - description: Starting timestamp of the logs that should be returned in milliseconds - in: query - name: limit + required: false + name: teamID schema: - default: 1000 - format: int32 - minimum: 0 - type: integer - description: Maximum number of logs that should be returned + type: string + description: Identifier of the team + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" responses: "200": - description: Successfully returned the sandbox logs + description: Successfully returned all templates + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: - $ref: "#/components/schemas/SandboxLogs" - "404": - $ref: "#/components/responses/404" + type: array + items: + $ref: "#/components/schemas/Template" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v2/sandboxes/{sandboxID}/logs: + /templates/{templateID}/files/{hash}: get: - summary: Sandbox logs (v2) - description: Get sandbox logs - tags: [sandboxes] + summary: Template build file upload URL + description: Get an upload link for a tar file containing build layer files + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" - - in: query - name: cursor - schema: - type: integer - format: int64 - minimum: 0 - description: Starting timestamp of the logs that should be returned in milliseconds - - in: query - name: limit - schema: - default: 1000 - type: integer - format: int32 - minimum: 0 - maximum: 1000 - description: Maximum number of logs that should be returned - - in: query - name: direction - schema: - $ref: "#/components/schemas/LogsDirection" - description: Direction of the logs that should be returned - - in: query - name: level - schema: - $ref: "#/components/schemas/LogLevel" - description: Minimum log level to return. Logs below this level are excluded - - in: query - name: search + - $ref: "#/components/parameters/templateID" + - in: path + name: hash + required: true schema: type: string - maxLength: 256 - description: Case-sensitive substring match on log message content + description: Hash of the files + responses: - "200": - description: Successfully returned the sandbox logs + "201": + description: The upload link where to upload the tar file content: application/json: schema: - $ref: "#/components/schemas/SandboxLogsV2Response" + $ref: "#/components/schemas/TemplateBuildFileUpload" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}: + /templates: get: - summary: Sandbox - description: Get a sandbox by id - tags: [sandboxes] + summary: List templates + x-api-group: list + description: List all templates + deprecated: true + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - in: query + required: false + name: teamID + schema: + type: string + description: Identifier of the team responses: "200": - description: Successfully returned the sandbox + description: Successfully returned all templates content: application/json: schema: - $ref: "#/components/schemas/SandboxDetail" - "404": - $ref: "#/components/responses/404" - "401": - $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" - - delete: - summary: Kill sandbox - description: Kill a sandbox - tags: [sandboxes] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/sandboxID" - responses: - "204": - description: The sandbox was killed successfully - "404": - $ref: "#/components/responses/404" + type: array + items: + $ref: "#/components/schemas/Template" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/metrics: + /templates/{templateID}: get: - summary: Sandbox metrics - description: Get sandbox metrics - tags: [sandboxes] + summary: List template builds + x-api-group: list + description: List all builds for a template + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" - - in: query - name: start - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the start of the interval, in seconds, for which the metrics - - in: query - name: end - schema: - type: integer - format: int64 - minimum: 0 - description: Unix timestamp for the end of the interval, in seconds, for which the metrics - + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" responses: "200": - description: Successfully returned the sandbox metrics + description: Successfully returned the template with its builds + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/SandboxMetric" - "400": - $ref: "#/components/responses/400" + $ref: "#/components/schemas/TemplateWithBuilds" "401": $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - - # TODO: Pause and resume might be exposed as POST /sandboxes/{sandboxID}/snapshot and then POST /sandboxes with specified snapshotting setup - /sandboxes/{sandboxID}/pause: - post: - summary: Pause sandbox - description: Pause the sandbox - tags: [sandboxes] + delete: + summary: Delete template + description: Delete a template + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/SandboxPauseRequest" + - $ref: "#/components/parameters/templateID" responses: "204": - description: The sandbox was paused successfully and can be resumed - "409": - $ref: "#/components/responses/409" - "404": - $ref: "#/components/responses/404" + description: The template was deleted successfully "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - - /sandboxes/{sandboxID}/resume: - post: - summary: Resume sandbox + patch: + summary: Update template + description: Update template deprecated: true - description: Resume the sandbox - tags: [sandboxes] + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - $ref: "#/components/parameters/templateID" requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ResumedSandbox" - responses: - "201": - description: The sandbox was resumed successfully - content: - application/json: - schema: - $ref: "#/components/schemas/Sandbox" - "409": - $ref: "#/components/responses/409" - "404": - $ref: "#/components/responses/404" + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TemplateUpdateRequest" + responses: + "200": + description: The template was updated successfully + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/fork: + /v2/templates/{templateID}/builds/{buildID}: post: - summary: Fork sandbox - description: >- - Fork the sandbox: checkpoint the running sandbox in place (it is - briefly paused, snapshotted with its full memory state, and resumed on - its node, keeping its ID and expiration untouched) and create count - new sandboxes from that snapshot. Returns one result per requested - fork, each carrying either the created sandbox or the error that - prevented it from starting. A non-201 status means the request failed - before any fork was attempted. - tags: [sandboxes] + summary: Start template build (v2) + description: Start the build + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" requestBody: - required: false + required: true content: application/json: schema: - $ref: "#/components/schemas/SandboxForkRequest" + $ref: "#/components/schemas/TemplateBuildStartV2" responses: - "201": - description: >- - The sandbox was snapshotted and the forks were attempted; each - entry reports one fork's outcome - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/SandboxForkResult" - "409": - $ref: "#/components/responses/409" - "404": - $ref: "#/components/responses/404" + "202": + description: The build has started + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/connect: - post: - summary: Connect sandbox - description: Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. - tags: [sandboxes] + /v2/templates/{templateID}: + patch: + summary: Update template (v2) + description: Update template + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - $ref: "#/components/parameters/templateID" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/ConnectSandbox" + $ref: "#/components/schemas/TemplateUpdateRequest" responses: "200": - description: The sandbox was already running - content: - application/json: - schema: - $ref: "#/components/schemas/Sandbox" - "201": - description: The sandbox was resumed successfully + description: The template was updated successfully content: application/json: schema: - $ref: "#/components/schemas/Sandbox" + $ref: "#/components/schemas/TemplateUpdateResponse" "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/timeout: - post: - summary: Set sandbox timeout - description: Set the timeout for the sandbox. The sandbox will expire x seconds from the time of the request. Calling this method multiple times overwrites the TTL, each time using the current timestamp as the starting point to measure the timeout duration. + /templates/{templateID}/builds/{buildID}/status: + get: + summary: Template build status + description: Get template build info + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - tags: [sandboxes] - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/SandboxTimeoutRequest" + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" + - in: query + name: logsOffset + schema: + default: 0 + type: integer + format: int32 + minimum: 0 + description: Index of the starting build log that should be returned with the template + - in: query + name: limit + schema: + default: 100 + type: integer + format: int32 + minimum: 0 + maximum: 100 + description: Maximum number of logs that should be returned + - in: query + name: level + schema: + $ref: "#/components/schemas/LogLevel" responses: - "204": - description: Successfully set the sandbox timeout + "200": + description: Successfully returned the template + content: + application/json: + schema: + $ref: "#/components/schemas/TemplateBuildInfo" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/network: - put: - summary: Update sandbox network - description: Update the network configuration for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting field clears it. + /templates/{templateID}/builds/{buildID}/logs: + get: + summary: Template build logs + description: Get template build logs + tags: [templates] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - tags: [sandboxes] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SandboxNetworkUpdateConfig" + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/sandboxID" + - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/buildID" + - in: query + name: cursor + schema: + type: integer + format: int64 + minimum: 0 + description: Starting timestamp of the logs that should be returned in milliseconds + - in: query + name: limit + schema: + default: 100 + type: integer + format: int32 + minimum: 0 + maximum: 100 + description: Maximum number of logs that should be returned + - in: query + name: direction + schema: + $ref: "#/components/schemas/LogsDirection" + - in: query + name: level + schema: + $ref: "#/components/schemas/LogLevel" + - in: query + name: source + schema: + $ref: "#/components/schemas/LogsSource" + description: Source of the logs that should be returned from responses: - "204": - description: Successfully updated the sandbox network configuration + "200": + description: Successfully returned the template build logs + content: + application/json: + schema: + $ref: "#/components/schemas/TemplateBuildLogsResponse" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" - "409": - $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /sandboxes/{sandboxID}/refreshes: + /templates/tags: post: - summary: Refresh sandbox - description: Refresh the sandbox extending its time to live + summary: Assign template tags + description: Assign tag(s) to a template build + tags: [tags] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - tags: [sandboxes] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: + required: true content: application/json: schema: - $ref: "#/components/schemas/SandboxRefreshRequest" - parameters: - - $ref: "#/components/parameters/sandboxID" + $ref: "#/components/schemas/AssignTemplateTagsRequest" responses: - "204": - description: Successfully refreshed the sandbox + "201": + description: Tag assigned successfully + content: + application/json: + schema: + $ref: "#/components/schemas/AssignedTemplateTags" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" - - /sandboxes/{sandboxID}/snapshots: - post: - summary: Create snapshot - description: Create a persistent snapshot from the sandbox's current state. Snapshots can be used to create new sandboxes and persist beyond the original sandbox's lifetime. - tags: [sandboxes] + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + delete: + summary: Delete template tags + description: Delete multiple tags from templates + tags: [tags] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/sandboxID" + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/SandboxSnapshotRequest" + $ref: "#/components/schemas/DeleteTemplateTagsRequest" responses: - "201": - description: Snapshot created successfully - content: - application/json: - schema: - $ref: "#/components/schemas/SnapshotInfo" + "204": + description: Tags deleted successfully "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /snapshots: + /templates/{templateID}/tags: get: - summary: List snapshots - description: List all snapshots for the team - tags: [snapshots] + summary: List template tags + x-api-group: list + description: List all tags for a template + tags: [tags] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: sandboxID - in: query - required: false - schema: - type: string - description: Filter snapshots by source sandbox ID - - name: name - in: query - description: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-team/my-snapshot" or "my-snapshot:v1"). - required: false - schema: - type: string - - $ref: "#/components/parameters/paginationLimit" - - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/templateID" responses: "200": - description: Successfully returned snapshots + description: Successfully returned the template tags content: application/json: schema: type: array items: - $ref: "#/components/schemas/SnapshotInfo" + $ref: "#/components/schemas/TemplateTag" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v3/templates: - post: - summary: Create template (v3) - description: Create a new template + /templates/aliases/{alias}: + get: + summary: Check template alias + description: Check if template with given alias exists tags: [templates] security: - ApiKeyAuth: [] @@ -2854,1076 +4251,1328 @@ paths: AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateBuildRequestV3" - + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - name: alias + in: path + required: true + schema: + type: string + description: Template alias responses: - "202": - description: The build was requested successfully + "200": + description: Successfully queried template by alias content: application/json: schema: - $ref: "#/components/schemas/TemplateRequestResponseV3" + $ref: "#/components/schemas/TemplateAliasResponse" "400": $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v2/templates: + /nodes: get: - summary: List templates (v2) - description: List all templates - tags: [templates] + summary: List nodes + description: List all nodes + tags: [admin] security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] - AdminTeamAuth: [] + - AdminJWTAuth: [] parameters: - in: query + name: clusterID + description: Identifier of the cluster required: false - name: teamID schema: type: string - description: Identifier of the team - - $ref: "#/components/parameters/paginationNextToken" - - $ref: "#/components/parameters/paginationLimit" + format: uuid responses: "200": - description: Successfully returned all templates - headers: - X-Next-Token: - description: Cursor to fetch the next page of results, if more exist - schema: - type: string + description: Successfully returned all nodes content: application/json: schema: type: array items: - $ref: "#/components/schemas/Template" - "400": - $ref: "#/components/responses/400" + $ref: "#/components/schemas/Node" "401": $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /nodes/{nodeID}: + get: + summary: Node info + description: Get node info + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/nodeID" + - in: query + name: clusterID + description: Identifier of the cluster + required: false + schema: + type: string + format: uuid + responses: + "200": + description: Successfully returned the node + content: + application/json: + schema: + $ref: "#/components/schemas/NodeDetail" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" post: - summary: Create template (v2) - description: Create a new template - deprecated: true - tags: [templates] + summary: Change node status + description: Change status of a node + tags: [admin] security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] - AdminTeamAuth: [] + - AdminJWTAuth: [] + parameters: + - $ref: "#/components/parameters/nodeID" requestBody: - required: true content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildRequestV2" - + $ref: "#/components/schemas/NodeStatusChange" responses: - "202": - description: The build was requested successfully - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateLegacy" - "400": - $ref: "#/components/responses/400" + "204": + description: The node status was changed successfully + "409": + $ref: "#/components/responses/409" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /templates/{templateID}/files/{hash}: - get: - summary: Template build file upload URL - description: Get an upload link for a tar file containing build layer files - tags: [templates] + /admin/teams/{teamID}/sandboxes/kill: + post: + summary: Kill all sandboxes for a team + description: Kills all sandboxes for the specified team + tags: [admin] security: - - AccessTokenAuth: [] - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] - AdminTeamAuth: [] + - AdminJWTAuth: [] parameters: - - $ref: "#/components/parameters/templateID" - - in: path - name: hash + - name: teamID + in: path required: true schema: type: string - description: Hash of the files - + format: uuid + description: Team ID responses: - "201": - description: The upload link where to upload the tar file + "200": + description: Successfully killed sandboxes content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildFileUpload" - "400": - $ref: "#/components/responses/400" + $ref: "#/components/schemas/AdminSandboxKillResult" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /templates: + /admin/sandboxes/running-counts: get: - summary: List templates - description: List all templates - deprecated: true - tags: [templates] + summary: Count running sandboxes by team + description: | + Returns a shared snapshot normally refreshed after five seconds. A + sandbox transitioning out of running can remain counted until removal. + tags: [admin] security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] - AdminTeamAuth: [] + - AdminJWTAuth: [] + responses: + "200": + description: Running sandbox counts keyed by team ID + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamRunningSandboxCounts" + "401": + $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /admin/teams/{teamID}/builds/cancel: + post: + summary: Cancel all builds for a team + description: Cancels all in-progress and pending builds for the specified team + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - in: query - required: false - name: teamID + - name: teamID + in: path + required: true schema: type: string - description: Identifier of the team + format: uuid + description: Team ID responses: "200": - description: Successfully returned all templates + description: Successfully cancelled builds content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/Template" + $ref: "#/components/schemas/AdminBuildCancelResult" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + + /admin/teams/{teamID}/api-keys: post: - summary: Create template - description: Create a new template - deprecated: true - tags: [templates] + summary: Create team API key as admin + description: Creates a team API key for internal service workflows. + tags: [admin] security: - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - name: teamID + in: path + required: true + schema: + type: string + format: uuid + description: Team ID requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildRequest" + $ref: "#/components/schemas/NewTeamAPIKey" + responses: + "201": + description: Team API key created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/CreatedTeamAPIKey" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + /admin/teams/{teamID}/api-keys/{apiKeyID}: + delete: + summary: Delete team API key as admin + description: Deletes a team API key for internal service workflows. + tags: [admin] + security: + - AdminApiKeyAuth: [] + - AdminJWTAuth: [] + parameters: + - name: teamID + in: path + required: true + schema: + type: string + format: uuid + description: Team ID + - $ref: "#/components/parameters/apiKeyID" responses: - "202": - description: The build was accepted - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateLegacy" + "204": + description: Team API key deleted successfully "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /templates/{templateID}: + /api-keys: get: - summary: List template builds - description: List all builds for a template - tags: [templates] + summary: List team API keys + x-api-group: list + description: List all team API keys + tags: [api-keys] security: - - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/paginationNextToken" - - $ref: "#/components/parameters/paginationLimit" + - AdminJWTAuth: [] + AdminTeamAuth: [] responses: "200": - description: Successfully returned the template with its builds + description: Successfully returned all team API keys content: application/json: schema: - $ref: "#/components/schemas/TemplateWithBuilds" + type: array + items: + $ref: "#/components/schemas/TeamAPIKey" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" post: - summary: Rebuild template - description: Rebuild an template - deprecated: true - tags: [templates] + summary: Create team API key + description: Create a new team API key + tags: [api-keys] security: - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildRequest" - + $ref: "#/components/schemas/NewTeamAPIKey" responses: - "202": - description: The build was accepted + "201": + description: Team API key created successfully content: application/json: schema: - $ref: "#/components/schemas/TemplateLegacy" - "401": - $ref: "#/components/responses/401" - "500": - $ref: "#/components/responses/500" - delete: - summary: Delete template - description: Delete a template - tags: [templates] - security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" - responses: - "204": - description: The template was deleted successfully + $ref: "#/components/schemas/CreatedTeamAPIKey" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + + /api-keys/{apiKeyID}: patch: - summary: Update template - description: Update template - deprecated: true - tags: [templates] + summary: Update team API key + description: Update a team API key + tags: [api-keys] security: - - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/apiKeyID" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/TemplateUpdateRequest" + $ref: "#/components/schemas/UpdateTeamAPIKey" responses: "200": - description: The template was updated successfully - "400": - $ref: "#/components/responses/400" + description: Team API key updated successfully "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - - /templates/{templateID}/builds/{buildID}: - post: - summary: Start template build - description: Start the build - deprecated: true - tags: [templates] + delete: + summary: Delete team API key + description: Delete a team API key + tags: [api-keys] security: - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/buildID" + - $ref: "#/components/parameters/apiKeyID" responses: - "202": - description: The build has started + "204": + description: Team API key deleted successfully "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v2/templates/{templateID}/builds/{buildID}: - post: - summary: Start template build (v2) - description: Start the build - tags: [templates] + /volumes: + get: + summary: List team volumes + x-api-group: list + description: List all team volumes + tags: [volumes] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/buildID" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateBuildStartV2" + - AdminJWTAuth: [] + AdminTeamAuth: [] responses: - "202": - description: The build has started + "200": + description: Successfully listed all team volumes + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Volume" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /v2/templates/{templateID}: - patch: - summary: Update template (v2) - description: Update template - tags: [templates] + post: + summary: Create team volume + description: Create a new team volume + tags: [volumes] security: - ApiKeyAuth: [] - - AccessTokenAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/templateID" + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/TemplateUpdateRequest" + $ref: "#/components/schemas/NewVolume" responses: - "200": - description: The template was updated successfully + "201": + description: Successfully created a new team volume content: application/json: schema: - $ref: "#/components/schemas/TemplateUpdateResponse" + $ref: "#/components/schemas/VolumeAndToken" "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /templates/{templateID}/builds/{buildID}/status: + /volumes/{volumeID}: get: - summary: Template build status - description: Get template build info - tags: [templates] + summary: Team volume + description: Get team volume info + tags: [volumes] security: - - AccessTokenAuth: [] - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/buildID" - - in: query - name: logsOffset - schema: - default: 0 - type: integer - format: int32 - minimum: 0 - description: Index of the starting build log that should be returned with the template - - in: query - name: limit - schema: - default: 100 - type: integer - format: int32 - minimum: 0 - maximum: 100 - description: Maximum number of logs that should be returned - - in: query - name: level - schema: - $ref: "#/components/schemas/LogLevel" + - $ref: "#/components/parameters/volumeID" responses: "200": - description: Successfully returned the template + description: Successfully retrieved a team volume content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildInfo" + $ref: "#/components/schemas/VolumeAndToken" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - /templates/{templateID}/builds/{buildID}/logs: + delete: + summary: Delete team volume + description: Delete a team volume + tags: [volumes] + security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/volumeID" + responses: + "204": + description: Successfully deleted a team volume + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" + + /secrets: get: - summary: Template build logs - description: Get template build logs - tags: [templates] + summary: List project secrets + x-api-group: list + description: List the project's secrets. No response carries a secret value. + tags: [secrets] security: - - AccessTokenAuth: [] - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/templateID" - - $ref: "#/components/parameters/buildID" - - in: query - name: cursor - schema: - type: integer - format: int64 - minimum: 0 - description: Starting timestamp of the logs that should be returned in milliseconds - - in: query - name: limit - schema: - default: 100 - type: integer - format: int32 - minimum: 0 - maximum: 100 - description: Maximum number of logs that should be returned - - in: query - name: direction - schema: - $ref: "#/components/schemas/LogsDirection" - - in: query - name: level - schema: - $ref: "#/components/schemas/LogLevel" - - in: query - name: source - schema: - $ref: "#/components/schemas/LogsSource" - description: Source of the logs that should be returned from + - $ref: "#/components/parameters/paginationNextToken" + - $ref: "#/components/parameters/paginationLimit" responses: "200": - description: Successfully returned the template build logs + description: Successfully listed the project's secrets + headers: + X-Next-Token: + $ref: "#/components/headers/XNextToken" content: application/json: schema: - $ref: "#/components/schemas/TemplateBuildLogsResponse" + type: array + items: + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" - /templates/tags: post: - summary: Assign template tags - description: Assign tag(s) to a template build - tags: [tags] + summary: Create a secret + description: Create a secret by storing a runtime marker as its first version. The response carries metadata only. + tags: [secrets] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/AssignTemplateTagsRequest" + $ref: "#/components/schemas/NewSecret" responses: "201": - description: Tag assigned successfully + description: Successfully created the secret content: application/json: schema: - $ref: "#/components/schemas/AssignedTemplateTags" + $ref: "#/components/schemas/Secret" "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - delete: - summary: Delete template tags - description: Delete multiple tags from templates - tags: [tags] + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" + + /secrets/{secretID}: + get: + summary: Get a secret + description: Get one secret's metadata, selected by identifier or name. + tags: [secrets] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/DeleteTemplateTagsRequest" + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/secretID" responses: - "204": - description: Tags deleted successfully + "200": + description: Successfully retrieved the secret + content: + application/json: + schema: + $ref: "#/components/schemas/Secret" "400": $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" - /templates/{templateID}/tags: - get: - summary: List template tags - description: List all tags for a template - tags: [tags] + post: + summary: Update a secret + description: Replace the secret's stored marker by appending a new version. The response carries metadata only. + tags: [secrets] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/templateID" + - $ref: "#/components/parameters/secretID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SecretUpdate" responses: "200": - description: Successfully returned the template tags + description: Successfully updated the secret content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/TemplateTag" + $ref: "#/components/schemas/Secret" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" - /templates/aliases/{alias}: - get: - summary: Check template alias - description: Check if template with given alias exists - tags: [templates] + delete: + summary: Delete a secret + description: Revoke the secret and schedule its versions for cleanup. + tags: [secrets] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: alias - in: path - required: true - schema: - type: string - description: Template alias + - $ref: "#/components/parameters/secretID" responses: - "200": - description: Successfully queried template by alias - content: - application/json: - schema: - $ref: "#/components/schemas/TemplateAliasResponse" + "204": + description: Successfully deleted the secret "400": $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" "403": $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "502": + $ref: "#/components/responses/502" + "504": + $ref: "#/components/responses/504" - /nodes: + /clusters/{clusterID}/rigs: get: - summary: List nodes - description: List all nodes + summary: List rigs of a cluster + description: > + List the orchestrator node pools ("rigs") of a cluster with a snapshot + of their scaling groups. Forwarded to the cluster's edge service; a + cluster with no rig management configured returns an empty list, and + the local cluster answers 501. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - in: query - name: clusterID - description: Identifier of the cluster - required: false - schema: - type: string - format: uuid + - $ref: "#/components/parameters/clusterID" responses: "200": - description: Successfully returned all nodes + description: Successfully returned the rigs of the cluster content: application/json: schema: type: array items: - $ref: "#/components/schemas/Node" + $ref: "#/components/schemas/Rig" "401": $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" - /nodes/{nodeID}: - get: - summary: Node info - description: Get node info + /clusters/{clusterID}/rigs/{rigID}/capacity: + put: + summary: Set the capacity of a rig + description: > + Set the desired instance count on the rig's scaling group. The value is + passed to the cloud provider unchanged; violations of the group's bounds + or conflicting concurrent operations surface as errors. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - $ref: "#/components/parameters/nodeID" - - in: query - name: clusterID - description: Identifier of the cluster - required: false - schema: - type: string - format: uuid + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RigCapacityChange" responses: - "200": - description: Successfully returned the node - content: - application/json: - schema: - $ref: "#/components/schemas/NodeDetail" + "202": + description: Capacity change accepted + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" - post: - summary: Change node status - description: Change status of a node + "501": + $ref: "#/components/responses/501" + + /clusters/{clusterID}/rigs/instances/{instanceID}: + delete: + summary: Terminate an instance of a rig + description: > + Terminate an instance in whichever rig's scaling group it belongs to. + The caller chooses whether the rig shrinks or the instance is replaced. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - $ref: "#/components/parameters/nodeID" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/NodeStatusChange" + - $ref: "#/components/parameters/clusterID" + - name: instanceID + in: path + required: true + schema: + type: string + description: Provider instance ID + - name: decrementDesired + in: query + required: true + schema: + type: boolean + description: > + When true, desired capacity is decremented (rig shrinks); + when false, the scaling group launches a replacement instance responses: - "204": - description: The node status was changed successfully - "409": - $ref: "#/components/responses/409" + "202": + description: Instance termination accepted + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "409": + $ref: "#/components/responses/409" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" - /admin/teams/{teamID}/sandboxes/kill: - post: - summary: Kill all sandboxes for a team - description: Kills all sandboxes for the specified team + /clusters/{clusterID}/rigs/{rigID}/instances: + get: + summary: List the instances attached to a rig + description: > + List the instances attached to the rig's scaling group with their + creation time and transition state, sorted by instance ID. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - name: teamID - in: path - required: true - schema: - type: string - format: uuid - description: Team ID + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" responses: "200": - description: Successfully killed sandboxes + description: Successfully returned the instances of the rig content: application/json: schema: - $ref: "#/components/schemas/AdminSandboxKillResult" + type: array + items: + $ref: "#/components/schemas/RigInstance" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" - /admin/teams/{teamID}/builds/cancel: - post: - summary: Cancel all builds for a team - description: Cancels all in-progress and pending builds for the specified team + /clusters/{clusterID}/rigs/{rigID}/errors: + get: + summary: List recent scaling errors of a rig + description: > + List recent scaling errors on the rig's scaling group (e.g. failed + instance creations due to resource exhaustion), newest first. tags: [admin] security: - AdminApiKeyAuth: [] + - AdminJWTAuth: [] parameters: - - name: teamID - in: path - required: true + - $ref: "#/components/parameters/clusterID" + - $ref: "#/components/parameters/rigID" + - name: limit + in: query + required: false schema: - type: string - format: uuid - description: Team ID + type: integer + format: int32 + minimum: 1 + maximum: 50 + default: 20 + description: Maximum number of errors to return responses: "200": - description: Successfully cancelled builds + description: Successfully returned the scaling errors of the rig content: application/json: schema: - $ref: "#/components/schemas/AdminBuildCancelResult" + type: array + items: + $ref: "#/components/schemas/RigError" + "400": + $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + "501": + $ref: "#/components/responses/501" - /admin/teams/{teamID}/api-keys: - post: - summary: Create team API key as admin - description: Creates a team API key for internal service workflows. - tags: [admin] + /events/sandboxes/{sandboxID}: + get: + description: Get sandbox events + tags: [events] security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: teamID - in: path - required: true + - $ref: "#/components/parameters/sandboxID" + - name: offset + in: query + required: false schema: - type: string - format: uuid - description: Team ID - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewTeamAPIKey" + type: integer + format: int32 + minimum: 0 + default: 0 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 10 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: types + in: query + required: false + style: form + explode: true + schema: + type: array + items: + type: string + description: Filter events to the provided event types responses: - "201": - description: Team API key created successfully + "200": + description: Successfully returned the sandbox events content: application/json: schema: - $ref: "#/components/schemas/CreatedTeamAPIKey" + type: array + items: + $ref: "#/components/schemas/SandboxEvent" "400": $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" - "403": - $ref: "#/components/responses/403" "404": $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - /admin/teams/{teamID}/api-keys/{apiKeyID}: - delete: - summary: Delete team API key as admin - description: Deletes a team API key for internal service workflows. - tags: [admin] + /events/sandboxes: + get: + description: Get all sandbox events for the team associated with the API key + tags: [events] security: + - ApiKeyAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - name: teamID - in: path - required: true + - name: offset + in: query + required: false schema: - type: string - format: uuid - description: Team ID - - $ref: "#/components/parameters/apiKeyID" + type: integer + format: int32 + minimum: 0 + default: 0 + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 10 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: types + in: query + required: false + style: form + explode: true + schema: + type: array + items: + type: string + description: Filter events to the provided event types responses: - "204": - description: Team API key deleted successfully + "200": + description: Successfully returned the sandbox events + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/SandboxEvent" "400": $ref: "#/components/responses/400" - "401": - $ref: "#/components/responses/401" "404": $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - /access-tokens: + /events/webhooks: post: - summary: Create access token - description: Create a new access token. Deprecated; use an API key (E2B_API_KEY) instead. - deprecated: true - tags: [access-tokens] + description: Register events webhook. + tags: [webhooks] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/NewAccessToken" + $ref: "#/components/schemas/WebhookCreate" responses: "201": - description: Access token created successfully + description: Successfully created webhook. content: application/json: schema: - $ref: "#/components/schemas/CreatedAccessToken" - "401": - $ref: "#/components/responses/401" - "410": - $ref: "#/components/responses/410" - "500": - $ref: "#/components/responses/500" - - /access-tokens/{accessTokenID}: - delete: - summary: Delete access token - description: Delete an access token - tags: [access-tokens] - security: - - AuthProviderBearerAuth: [] - parameters: - - $ref: "#/components/parameters/accessTokenID" - responses: - "204": - description: Access token deleted successfully - "401": - $ref: "#/components/responses/401" + $ref: "#/components/schemas/WebhookCreation" + "400": + $ref: "#/components/responses/400" "404": $ref: "#/components/responses/404" + "401": + $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - - /api-keys: get: - summary: List team API keys - description: List all team API keys - tags: [api-keys] + description: List registered webhooks. + tags: [webhooks] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] responses: "200": - description: Successfully returned all team API keys + description: List of registered webhooks. content: application/json: schema: type: array items: - $ref: "#/components/schemas/TeamAPIKey" + $ref: "#/components/schemas/WebhookDetail" + "404": + $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - post: - summary: Create team API key - description: Create a new team API key - tags: [api-keys] + + /events/webhooks/{webhookID}: + get: + description: Get a registered webhook. + tags: [webhooks] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewTeamAPIKey" + - AdminApiKeyAuth: [] + AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" responses: - "201": - description: Team API key created successfully + "200": + description: Successfully returned the webhook configuration. content: application/json: schema: - $ref: "#/components/schemas/CreatedTeamAPIKey" + $ref: "#/components/schemas/WebhookDetail" + "404": + $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - /api-keys/{apiKeyID}: patch: - summary: Update team API key - description: Update a team API key - tags: [api-keys] + description: Update a registered webhook configuration. + tags: [webhooks] security: + - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/apiKeyID" + - $ref: "#/components/parameters/webhookID" requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/UpdateTeamAPIKey" + $ref: "#/components/schemas/WebhookConfiguration" responses: "200": - description: Team API key updated successfully - "401": - $ref: "#/components/responses/401" + description: Successfully updated webhook. + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookDetail" + "400": + $ref: "#/components/responses/400" "404": $ref: "#/components/responses/404" - "500": - $ref: "#/components/responses/500" - delete: - summary: Delete team API key - description: Delete a team API key - tags: [api-keys] - security: - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/apiKeyID" - responses: - "204": - description: Team API key deleted successfully "401": $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" - /volumes: - get: - summary: List team volumes - description: List all team volumes - tags: [volumes] + delete: + description: Delete a registered webhook. + tags: [webhooks] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" responses: "200": - description: Successfully listed all team volumes - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/Volume" + description: Successfully deleted webhook. + "404": + $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - post: - summary: Create team volume - description: Create a new team volume - tags: [volumes] + /events/webhooks/{webhookID}/deliveries: + get: + description: List webhook delivery attempts. + tags: [webhooks] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NewVolume" + - AdminJWTAuth: [] + AdminTeamAuth: [] + parameters: + - $ref: "#/components/parameters/webhookID" + - name: cursor + in: query + required: false + schema: + type: string + description: Opaque cursor from the previous response's nextCursor field. + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 25 + - name: orderAsc + in: query + required: false + schema: + type: boolean + default: false + - name: start + in: query + required: false + schema: + type: string + format: date-time + description: Include deliveries at or after this timestamp. + - name: end + in: query + required: false + schema: + type: string + format: date-time + description: Include deliveries before this timestamp. + - name: deliveryStatus + in: query + required: false + style: form + explode: false + schema: + type: array + items: + type: string + enum: [success, failed] + description: Filter deliveries by delivery status + - name: eventType + in: query + required: false + style: form + explode: false + schema: + type: array + items: + type: string + description: Filter deliveries by event type responses: - "201": - description: Successfully created a new team volume + "200": + description: List of webhook delivery attempts grouped by event. content: application/json: schema: - $ref: "#/components/schemas/VolumeAndToken" + $ref: "#/components/schemas/WebhookDeliveriesListPayload" "400": $ref: "#/components/responses/400" + "404": + $ref: "#/components/responses/404" "401": $ref: "#/components/responses/401" "500": $ref: "#/components/responses/500" - /volumes/{volumeID}: + /events/webhooks/{webhookID}/stats: get: - summary: Team volume - description: Get team volume info - tags: [volumes] + description: Get webhook delivery aggregate stats. + tags: [webhooks] security: - ApiKeyAuth: [] - AuthProviderBearerAuth: [] AuthProviderTeamAuth: [] - AdminApiKeyAuth: [] AdminTeamAuth: [] + - AdminJWTAuth: [] + AdminTeamAuth: [] parameters: - - $ref: "#/components/parameters/volumeID" + - $ref: "#/components/parameters/webhookID" + - name: start + in: query + required: false + schema: + type: string + format: date-time + description: Inclusive stats range start. Defaults to 24 hours ago. + - name: end + in: query + required: false + schema: + type: string + format: date-time + description: Exclusive stats range end. Defaults to now. responses: "200": - description: Successfully retrieved a team volume + description: Webhook delivery stats. content: application/json: schema: - $ref: "#/components/schemas/VolumeAndToken" - "401": - $ref: "#/components/responses/401" + $ref: "#/components/schemas/WebhookDeliveryStats" "404": $ref: "#/components/responses/404" - "500": - $ref: "#/components/responses/500" - - delete: - summary: Delete team volume - description: Delete a team volume - tags: [volumes] - security: - - ApiKeyAuth: [] - - AuthProviderBearerAuth: [] - AuthProviderTeamAuth: [] - - AdminApiKeyAuth: [] - AdminTeamAuth: [] - parameters: - - $ref: "#/components/parameters/volumeID" - responses: - "204": - description: Successfully deleted a team volume "401": $ref: "#/components/responses/401" - "404": - $ref: "#/components/responses/404" "500": $ref: "#/components/responses/500" diff --git a/src/core/shared/contracts/dashboard-api.types.ts b/src/core/shared/contracts/dashboard-api.types.ts index abe271801..31a13c362 100644 --- a/src/core/shared/contracts/dashboard-api.types.ts +++ b/src/core/shared/contracts/dashboard-api.types.ts @@ -277,6 +277,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 412: components['responses']['412'] 500: components['responses']['500'] } } @@ -320,6 +321,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 412: components['responses']['412'] 500: components['responses']['500'] } } @@ -366,6 +368,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 412: components['responses']['412'] 500: components['responses']['500'] 502: components['responses']['502'] } @@ -376,6 +379,390 @@ export interface paths { patch?: never trace?: never } + '/admin/clusters': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Create a cluster + * @description Creates a cluster whose configuration cannot be modified. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminClusterCreateRequest'] + } + } + responses: { + /** @description Cluster created. */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminClusterCreateResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/clusters/{clusterID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + post?: never + /** + * Delete an unreferenced cluster + * @description Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Cluster deleted or already absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/v1/management/clusters/{clusterID}/destroy-readiness': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Check cluster destroy readiness + * @description Checks whether this exact cluster has active templates or snapshots. Soft-deleted history and team assignments do not block this check. Returns success if the cluster is absent. This read does not change resources or prevent later template creation. + */ + get: operations['managementClusterDestroyReadiness'] + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/cluster': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Get a team's assigned cluster + * @description Returns the current cluster assignment without exposing cluster credentials. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Cluster assignment returned. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminTeamClusterAssignmentResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + /** + * Assign a cluster to a team + * @description Updates the team's cluster reference to an existing cluster. New and replacement assignments require a tier identifier containing `enterprise`, case-insensitively. Replaying the identical assignment succeeds even if the team's tier later changes. + */ + put: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminTeamClusterAssignmentRequest'] + } + } + responses: { + /** @description Cluster assigned. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 412: components['responses']['412'] + 500: components['responses']['500'] + } + } + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/cluster/{clusterID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + post?: never + /** + * Detach a cluster from a team + * @description Clears the assignment only when the team is unassigned or assigned to the specified cluster. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Cluster detached or the team was already unassigned. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 412: components['responses']['412'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/ban': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + /** + * Ban a team + * @description Marks the team as banned so its API keys stop authenticating. Idempotent; running workloads are not touched. + */ + put: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Team banned. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + post?: never + /** + * Unban a team + * @description Clears the team's ban. Idempotent. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Team unbanned. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/block': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + /** + * Block a team + * @description Marks the team as blocked with the given reason, so it can no longer start sandboxes or builds. Idempotent; a repeated call replaces the reason. + */ + put: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminTeamBlockRequest'] + } + } + responses: { + /** @description Team blocked. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + post?: never + /** + * Unblock a team + * @description Clears the team's block and its recorded reason. Idempotent. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Team unblocked. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } '/admin/user-profiles/resolve': { parameters: { query?: never @@ -541,6 +928,7 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 412: components['responses']['412'] 500: components['responses']['500'] } } @@ -637,9 +1025,100 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 412: components['responses']['412'] + 500: components['responses']['500'] + } + } + trace?: never + } + '/teams/{teamID}/status': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Get team access status + * @description Returns whether the team is blocked or banned and its recorded blocked reason. Team-authenticated requests may read only the team they are scoped to. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned team access status. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['TeamStatusResponse'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/teams/{teamID}/limits': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * Get team limits + * @description Returns the team's tier and effective resource limits. Team-authenticated requests may read only the team they are scoped to. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the team. */ + teamID: components['parameters']['teamID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned team limits. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['TeamLimitsResponse'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] 500: components['responses']['500'] } } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never trace?: never } '/teams/{teamID}/members': { @@ -705,6 +1184,7 @@ export interface paths { 401: components['responses']['401'] 403: components['responses']['403'] 404: components['responses']['404'] + 412: components['responses']['412'] 500: components['responses']['500'] } } @@ -749,6 +1229,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 412: components['responses']['412'] 500: components['responses']['500'] } } @@ -1106,63 +1587,68 @@ export interface paths { patch?: never trace?: never } - '/admin/v1/projects/{teamID}': { + '/v1/management/projects/{projectID}': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } get?: never - /** Create or reconcile a project. */ - put: operations['upsertProject'] + /** Create or reconcile a project (v1). */ + put: operations['managementUpsertProject'] post?: never - /** Delete a project and its control-plane state. */ - delete: operations['deleteProject'] + /** + * Delete a project and its control-plane state (v1). + * @description Declared, and answered with 501 by every control plane. Deleting a project means reclaiming templates, snapshots, volumes, running sandboxes and their stored artifacts, and no single service can reach all of them today. Callers should not depend on this operation until that changes. + */ + delete: operations['managementDeleteProject'] options?: never head?: never patch?: never trace?: never } - '/admin/v1/projects/{teamID}/members/{userId}': { + '/v1/management/projects/{projectID}/members/{userID}': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + userID: components['parameters']['userID'] } cookie?: never } get?: never - /** Reconcile an opaque user UUID as a project member. */ - put: operations['upsertProjectMember'] + /** + * Apply one versioned project member projection (v1). + * @description Applies the newest desired presence for one project member. An older or duplicate revision is accepted without changing target state. + */ + put: operations['managementApplyProjectMember'] post?: never - /** Remove a project member. */ - delete: operations['deleteProjectMember'] + delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/v1/projects/{teamID}/limits': { + '/v1/management/projects/{projectID}/limits': { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } get?: never - /** Reconcile effective limits for a project. */ - put: operations['upsertProjectLimits'] + /** Reconcile effective limits for a project (v1). */ + put: operations['managementUpsertProjectLimits'] post?: never delete?: never options?: never @@ -1170,21 +1656,51 @@ export interface paths { patch?: never trace?: never } - '/admin/v1/users/{userId}': { + '/v1/management/clusters/{clusterID}': { parameters: { query?: never header?: never path: { - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] } cookie?: never } get?: never - put?: never + /** Register a cluster (v1). */ + put: operations['managementRegisterCluster'] + post?: never + /** + * Delete an unreferenced cluster (v1). + * @description Deletes a cluster after all team assignments are detached and no active environment references remain. Releases soft-deleted environment references in the same transaction while preserving environment and build history. Repeating a completed deletion succeeds. + */ + delete: operations['managementDeleteCluster'] + options?: never + head?: never + patch?: never + trace?: never + } + '/v1/management/projects/{projectID}/cluster/{clusterID}': { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + get?: never + /** + * Assign a cluster to a project (v1). + * @description Assigns the cluster only when the project's tier identifier contains `enterprise`, case-insensitively. Replaying the identical assignment succeeds even if the project's tier later changes. + */ + put: operations['managementAssignProjectCluster'] post?: never - /** Purge shard-local membership and access-token state for an opaque user UUID. */ - delete: operations['purgeUser'] + /** Detach a cluster assignment from a project (v1). */ + delete: operations['managementDetachProjectCluster'] options?: never head?: never patch?: never @@ -1240,6 +1756,40 @@ export interface components { */ email: string } + AdminClusterCreateRequest: { + /** + * Format: uuid + * @description Optional stable identifier for idempotent creation. Reuse succeeds only when the immutable configuration is identical. + */ + cluster_id?: string + name: string + endpoint: string + endpoint_tls: boolean + token: string + sandbox_proxy_domain?: string | null + auth_org_id?: string | null + } + AdminClusterCreateResponse: { + /** Format: uuid */ + cluster_id: string + } + AdminTeamBlockRequest: { + /** @description Shown to the team as the reason it is blocked. */ + reason: string + } + AdminTeamClusterAssignmentRequest: { + /** Format: uuid */ + cluster_id: string + /** + * @description Assign only when the team is unassigned or already assigned to cluster_id. + * @default false + */ + preserve_existing: boolean + } + AdminTeamClusterAssignmentResponse: { + /** Format: uuid */ + cluster_id: string + } /** * @description Build status mapped for dashboard clients. * @enum {string} @@ -1376,19 +1926,24 @@ export interface components { UserTeamLimits: { /** Format: int64 */ maxLengthHours: number - /** Format: int32 */ + /** Format: int64 */ concurrentSandboxes: number - /** Format: int32 */ + /** Format: int64 */ concurrentTemplateBuilds: number - /** Format: int32 */ + /** Format: int64 */ maxVcpu: number - /** Format: int32 */ + /** Format: int64 */ maxRamMb: number - /** Format: int32 */ + /** Format: int64 */ diskMb: number - /** Format: int32 */ + /** Format: int64 */ eventsTtlDays: number } + TeamLimitsResponse: { + /** @description The team's raw tier identifier, exactly as stored (not normalized to a catalog plan). */ + tier: string + limits: components['schemas']['UserTeamLimits'] + } UserTeam: { /** Format: uuid */ id: string @@ -1396,7 +1951,6 @@ export interface components { slug: string tier: string email: string - profilePictureUrl: string | null isBlocked: boolean isBanned: boolean blockedReason: string | null @@ -1408,6 +1962,11 @@ export interface components { UserTeamsResponse: { teams: components['schemas']['UserTeam'][] } + TeamStatusResponse: { + isBlocked: boolean + isBanned: boolean + blockedReason: string | null + } TeamMember: { /** Format: uuid */ id: string @@ -1427,13 +1986,11 @@ export interface components { } UpdateTeamRequest: { name?: string - profilePictureUrl?: string | null } UpdateTeamResponse: { /** Format: uuid */ id: string name: string - profilePictureUrl?: string | null } AddTeamMemberRequest: { /** Format: email */ @@ -1619,37 +2176,93 @@ export interface components { id: string slug: string } - /** @enum {string} */ - AdminControlPlaneProjectType: 'development' | 'staging' | 'production' - AdminControlPlaneProjectUpsertRequest: { + /** + * @description The properties of a project this side stores. Every one is synchronized by the caller and sent on every push, so a reconcile is a complete statement of the project rather than a patch. + * + * A project's tier is not among them. It is assigned once, at creation, from this side's own default, and no push moves it — limits arrive separately and in full through upsertProjectLimits, which takes precedence over the tier anyway. + */ + ManagementProjectUpsertRequest: { name: string + /** @description Changing it renames the project, and nothing follows it. Template names embed the slug they were built under, so a renamed project keeps its existing template names and only new ones carry the new slug. A slug already held on this control plane is a 409, on a rename as much as on a create. */ slug: string - project_type: components['schemas']['AdminControlPlaneProjectType'] + /** @description Contact address recorded on the project. */ + email: string } - AdminControlPlaneProject: components['schemas']['AdminControlPlaneProjectUpsertRequest'] & { + ManagementProject: components['schemas']['ManagementProjectUpsertRequest'] & { /** Format: uuid */ id: string } - AdminControlPlaneMemberUpsertRequest: { - /** Format: uuid */ - added_by?: string + ManagementClusterRegistrationRequest: { + name: string + endpoint: string + endpoint_tls: boolean + token: string + sandbox_proxy_domain?: string | null + auth_org_id?: string | null } - AdminControlPlaneProjectLimits: { - /** Format: int32 */ - concurrent_sandboxes: number - /** Format: int32 */ - max_sandbox_length_hours: number - /** Format: int32 */ - max_vcpu: number - /** Format: int64 */ - max_ram_mb: number + ManagementProjectMemberIdentity: { + issuer: string + subject: string + } + ManagementProjectMemberApplyRequest: { /** Format: int64 */ - disk_mb: number - /** Format: int32 */ - concurrent_template_builds: number - /** Format: int32 */ - events_ttl_days: number + revision: number + present: boolean + /** + * @description Whether this membership is the user's default team. Omitted requests from older sources remain non-default. + * @default false + */ + is_default: boolean + identities?: components['schemas']['ManagementProjectMemberIdentity'][] } + /** + * @description A project's effective limits, already resolved by the caller. Every field is absolute: this side stores what it is given and performs no arithmetic of its own. + * + * The minimums below track the CHECK constraints on tiers, which is the contract for what a limit may be. project_limits stores the same values under looser constraints on purpose — it is a push target, and a floor that only rejects the impossible keeps a future decision about what is allowed a change to this schema rather than a migration. + * + * `max_disk_size_mb` and `max_free_disk_size_mb` are one ceiling under two names, and either name alone carries it. A caller that sends both must send them equal; a caller that sends neither is refused. Sending both is what a caller does while receivers older than the second name are still running. + */ + ManagementProjectLimits: + | { + /** + * Format: int64 + * @description The caller's version of this answer, raised whenever the limits it resolved for the project change. Delivery is over a network, so two pushes can be in flight at once and arrive in either order: this side stores the revision it accepted and drops a delivery at or below it, which is what keeps a delayed retry from putting the project back on limits it has already left. + * + * Comparable only against earlier revisions for the same project. + */ + revision: number + /** Format: int32 */ + concurrent_sandboxes: number + /** Format: int32 */ + max_sandbox_length_hours: number + /** Format: int32 */ + max_vcpu: number + /** Format: int64 */ + max_ram_mb: number + /** Format: int64 */ + disk_mb: number + /** Format: int32 */ + concurrent_template_builds: number + /** Format: int32 */ + events_ttl_days: number + /** + * Format: int64 + * @description The default free-space growth target when a template build request omits one. May sit anywhere at or below the maximum free-space growth target, and usually sits well below it; a delivery whose default exceeds the maximum is rejected. + */ + default_free_disk_size_mb: number + /** + * Format: int64 + * @description The most a template build may request as its free-space growth target. + */ + max_free_disk_size_mb?: number + /** + * Format: int64 + * @description The same ceiling as max_free_disk_size_mb, under the name it was first published with. Kept until every deployed sender and receiver speaks the other name. + */ + max_disk_size_mb?: number + } + | unknown + | unknown } responses: { /** @description Bad request */ @@ -1697,6 +2310,15 @@ export interface components { 'application/json': components['schemas']['Error'] } } + /** @description Legacy team mutations are disabled */ + 412: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } /** @description Server error */ 500: { headers: { @@ -1742,6 +2364,12 @@ export interface components { build_ids: string[] /** @description Identifier of the team. */ teamID: string + /** @description Identifier of the cluster. */ + clusterID: string + /** @description Identifier of the project. */ + projectID: string + /** @description Identifier of the user. */ + userID: string /** @description Identifier of the user. */ userId: string /** @description Team slug to resolve. */ @@ -1787,19 +2415,52 @@ export interface components { } export type $defs = Record export interface operations { - upsertProject: { + managementClusterDestroyReadiness: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description No active templates or snapshots reference the cluster. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + /** @description Active templates or snapshots must be deleted before destroying the cluster. */ + 409: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + 500: components['responses']['500'] + } + } + managementUpsertProject: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneProjectUpsertRequest'] + 'application/json': components['schemas']['ManagementProjectUpsertRequest'] } } responses: { @@ -1809,7 +2470,7 @@ export interface operations { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminControlPlaneProject'] + 'application/json': components['schemas']['ManagementProject'] } } /** @description Project created. */ @@ -1818,7 +2479,7 @@ export interface operations { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminControlPlaneProject'] + 'application/json': components['schemas']['ManagementProject'] } } 400: components['responses']['400'] @@ -1828,13 +2489,13 @@ export interface operations { 501: components['responses']['501'] } } - deleteProject: { + managementDeleteProject: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } @@ -1853,25 +2514,25 @@ export interface operations { 501: components['responses']['501'] } } - upsertProjectMember: { + managementApplyProjectMember: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + userID: components['parameters']['userID'] } cookie?: never } - requestBody?: { + requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneMemberUpsertRequest'] + 'application/json': components['schemas']['ManagementProjectMemberApplyRequest'] } } responses: { - /** @description Membership is present. */ + /** @description Membership projection is applied or already superseded. */ 204: { headers: { [name: string]: unknown @@ -1881,25 +2542,27 @@ export interface operations { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 409: components['responses']['409'] 500: components['responses']['500'] - 501: components['responses']['501'] } } - deleteProjectMember: { + managementUpsertProjectLimits: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] } cookie?: never } - requestBody?: never + requestBody: { + content: { + 'application/json': components['schemas']['ManagementProjectLimits'] + } + } responses: { - /** @description Membership is absent. */ + /** @description Effective limits are synchronized. */ 204: { headers: { [name: string]: unknown @@ -1913,23 +2576,23 @@ export interface operations { 501: components['responses']['501'] } } - upsertProjectLimits: { + managementRegisterCluster: { parameters: { query?: never header?: never path: { - /** @description Identifier of the team. */ - teamID: components['parameters']['teamID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] } cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['AdminControlPlaneProjectLimits'] + 'application/json': components['schemas']['ManagementClusterRegistrationRequest'] } } responses: { - /** @description Effective limits are synchronized. */ + /** @description Cluster registration is present. */ 204: { headers: { [name: string]: unknown @@ -1938,24 +2601,49 @@ export interface operations { } 400: components['responses']['400'] 401: components['responses']['401'] - 404: components['responses']['404'] + 409: components['responses']['409'] 500: components['responses']['500'] - 501: components['responses']['501'] } } - purgeUser: { + managementDeleteCluster: { parameters: { query?: never header?: never path: { - /** @description Identifier of the user. */ - userId: components['parameters']['userId'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Cluster is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + managementAssignProjectCluster: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] } cookie?: never } requestBody?: never responses: { - /** @description User-owned shard state is absent. */ + /** @description Cluster is assigned to the project. */ 204: { headers: { [name: string]: unknown @@ -1964,8 +2652,36 @@ export interface operations { } 400: components['responses']['400'] 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 500: components['responses']['500'] + } + } + managementDetachProjectCluster: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the project. */ + projectID: components['parameters']['projectID'] + /** @description Identifier of the cluster. */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description The matching assignment is absent. */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] 500: components['responses']['500'] - 501: components['responses']['501'] } } } diff --git a/src/core/shared/contracts/infra-api.types.ts b/src/core/shared/contracts/infra-api.types.ts index 4e306d805..18de0363b 100644 --- a/src/core/shared/contracts/infra-api.types.ts +++ b/src/core/shared/contracts/infra-api.types.ts @@ -32,6 +32,7 @@ export interface paths { content?: never } 401: components['responses']['401'] + 429: components['responses']['429'] } } put?: never @@ -72,6 +73,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -121,6 +123,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -172,6 +175,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -218,13 +222,15 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never /** * Create sandbox - * @description Create a sandbox from the template + * @deprecated + * @description Create a sandbox from the template. Use POST /v2/sandboxes instead. */ post: { parameters: { @@ -250,7 +256,10 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -277,6 +286,12 @@ export interface paths { metadata?: string /** @description Filter sandboxes by one or more states */ state?: components['schemas']['SandboxState'][] + /** @description Sort direction by sandbox start time. Defaults to desc (newest first). */ + order?: components['schemas']['OrderDirection'] + /** @description Return sandboxes started at or after this timestamp. */ + startedAfter?: string + /** @description Filter sandboxes by a template ID or alias. */ + template?: string /** @description Cursor to start the list from */ nextToken?: components['parameters']['paginationNextToken'] /** @description Maximum number of items to return per page */ @@ -291,6 +306,8 @@ export interface paths { /** @description Successfully returned all running sandboxes */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] + 'X-Total-Running': components['headers']['XTotalRunning'] [name: string]: unknown } content: { @@ -299,11 +316,45 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - post?: never + /** + * Create sandbox (v2) + * @description Create a sandbox from the template. All system communication with the sandbox is secured. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['NewSandboxV2'] + } + } + responses: { + /** @description The sandbox was created successfully */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] + } + } delete?: never options?: never head?: never @@ -344,6 +395,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -394,6 +446,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -449,6 +502,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -493,6 +547,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -522,6 +577,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -568,6 +624,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -617,7 +674,9 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] } } delete?: never @@ -649,7 +708,7 @@ export interface paths { } cookie?: never } - requestBody: { + requestBody?: { content: { 'application/json': components['schemas']['ResumedSandbox'] } @@ -664,10 +723,14 @@ export interface paths { 'application/json': components['schemas']['Sandbox'] } } + 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -716,7 +779,9 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] } } delete?: never @@ -736,7 +801,8 @@ export interface paths { put?: never /** * Connect sandbox - * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. + * @deprecated + * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. Use POST /v2/sandboxes/{sandboxID}/connect instead. */ post: { parameters: { @@ -774,7 +840,73 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/v2/sandboxes/{sandboxID}/connect': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Connect sandbox (v2) + * @description Returns sandbox details. If the sandbox is paused, it will be resumed. TTL is only extended. The request body is optional; an omitted timeout defaults to 300 seconds. + */ + post: { + parameters: { + query?: never + header?: never + path: { + sandboxID: components['parameters']['sandboxID'] + } + cookie?: never + } + requestBody?: { + content: { + 'application/json': components['schemas']['ConnectSandboxV2'] + } + } + responses: { + /** @description The sandbox was already running */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + /** @description The sandbox was resumed successfully */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Sandbox'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] + 503: components['responses']['503'] + 504: components['responses']['504'] } } delete?: never @@ -820,6 +952,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -866,6 +999,7 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -913,6 +1047,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] } } delete?: never @@ -961,6 +1096,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1001,6 +1137,7 @@ export interface paths { /** @description Successfully returned snapshots */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1008,6 +1145,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1057,6 +1195,8 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1095,8 +1235,7 @@ export interface paths { /** @description Successfully returned all templates */ 200: { headers: { - /** @description Cursor to fetch the next page of results, if more exist */ - 'X-Next-Token'?: string + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1106,42 +1245,12 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 403: components['responses']['403'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Create template (v2) - * @deprecated - * @description Create a new template - */ - post: { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequestV2'] - } - } - responses: { - /** @description The build was requested successfully */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 400: components['responses']['400'] - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never delete?: never options?: never head?: never @@ -1183,6 +1292,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1227,42 +1337,12 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Create template - * @deprecated - * @description Create a new template - */ - post: { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequest'] - } - } - responses: { - /** @description The build was accepted */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 400: components['responses']['400'] - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never delete?: never options?: never head?: never @@ -1299,6 +1379,7 @@ export interface paths { /** @description Successfully returned the template with its builds */ 200: { headers: { + 'X-Next-Token': components['headers']['XNextToken'] [name: string]: unknown } content: { @@ -1306,43 +1387,12 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } put?: never - /** - * Rebuild template - * @deprecated - * @description Rebuild an template - */ - post: { - parameters: { - query?: never - header?: never - path: { - templateID: components['parameters']['templateID'] - } - cookie?: never - } - requestBody: { - content: { - 'application/json': components['schemas']['TemplateBuildRequest'] - } - } - responses: { - /** @description The build was accepted */ - 202: { - headers: { - [name: string]: unknown - } - content: { - 'application/json': components['schemas']['TemplateLegacy'] - } - } - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } + post?: never /** * Delete template * @description Delete a template @@ -1366,6 +1416,7 @@ export interface paths { content?: never } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1400,54 +1451,12 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } trace?: never } - '/templates/{templateID}/builds/{buildID}': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - get?: never - put?: never - /** - * Start template build - * @deprecated - * @description Start the build - */ - post: { - parameters: { - query?: never - header?: never - path: { - templateID: components['parameters']['templateID'] - buildID: components['parameters']['buildID'] - } - cookie?: never - } - requestBody?: never - responses: { - /** @description The build has started */ - 202: { - headers: { - [name: string]: unknown - } - content?: never - } - 401: components['responses']['401'] - 500: components['responses']['500'] - } - } - delete?: never - options?: never - head?: never - patch?: never - trace?: never - } '/v2/templates/{templateID}/builds/{buildID}': { parameters: { query?: never @@ -1484,7 +1493,9 @@ export interface paths { } content?: never } + 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1537,6 +1548,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1582,6 +1594,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1636,6 +1649,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1685,6 +1699,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1715,6 +1730,7 @@ export interface paths { 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1757,6 +1773,7 @@ export interface paths { 401: components['responses']['401'] 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1802,6 +1819,7 @@ export interface paths { 400: components['responses']['400'] 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1846,6 +1864,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1893,6 +1912,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1926,6 +1946,7 @@ export interface paths { 401: components['responses']['401'] 404: components['responses']['404'] 409: components['responses']['409'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1971,6 +1992,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -1980,52 +2002,50 @@ export interface paths { patch?: never trace?: never } - '/admin/teams/{teamID}/builds/cancel': { + '/admin/sandboxes/running-counts': { parameters: { query?: never header?: never path?: never cookie?: never } - get?: never - put?: never /** - * Cancel all builds for a team - * @description Cancels all in-progress and pending builds for the specified team + * Count running sandboxes by team + * @description Returns a shared snapshot normally refreshed after five seconds. A + * sandbox transitioning out of running can remain counted until removal. */ - post: { + get: { parameters: { query?: never header?: never - path: { - /** @description Team ID */ - teamID: string - } + path?: never cookie?: never } requestBody?: never responses: { - /** @description Successfully cancelled builds */ + /** @description Running sandbox counts keyed by team ID */ 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['AdminBuildCancelResult'] + 'application/json': components['schemas']['AdminTeamRunningSandboxCounts'] } } 401: components['responses']['401'] - 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } + put?: never + post?: never delete?: never options?: never head?: never patch?: never trace?: never } - '/admin/teams/{teamID}/api-keys': { + '/admin/teams/{teamID}/builds/cancel': { parameters: { query?: never header?: never @@ -2035,8 +2055,8 @@ export interface paths { get?: never put?: never /** - * Create team API key as admin - * @description Creates a team API key for internal service workflows. + * Cancel all builds for a team + * @description Cancels all in-progress and pending builds for the specified team */ post: { parameters: { @@ -2048,25 +2068,20 @@ export interface paths { } cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['NewTeamAPIKey'] - } - } + requestBody?: never responses: { - /** @description Team API key created successfully */ - 201: { + /** @description Successfully cancelled builds */ + 200: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['CreatedTeamAPIKey'] + 'application/json': components['schemas']['AdminBuildCancelResult'] } } - 400: components['responses']['400'] 401: components['responses']['401'] - 403: components['responses']['403'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2076,7 +2091,7 @@ export interface paths { patch?: never trace?: never } - '/admin/teams/{teamID}/api-keys/{apiKeyID}': { + '/admin/teams/{teamID}/api-keys': { parameters: { query?: never header?: never @@ -2085,80 +2100,40 @@ export interface paths { } get?: never put?: never - post?: never /** - * Delete team API key as admin - * @description Deletes a team API key for internal service workflows. + * Create team API key as admin + * @description Creates a team API key for internal service workflows. */ - delete: { + post: { parameters: { query?: never header?: never path: { /** @description Team ID */ teamID: string - apiKeyID: components['parameters']['apiKeyID'] - } - cookie?: never - } - requestBody?: never - responses: { - /** @description Team API key deleted successfully */ - 204: { - headers: { - [name: string]: unknown - } - content?: never } - 400: components['responses']['400'] - 401: components['responses']['401'] - 404: components['responses']['404'] - 500: components['responses']['500'] - } - } - options?: never - head?: never - patch?: never - trace?: never - } - '/access-tokens': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - get?: never - put?: never - /** - * Create access token - * @deprecated - * @description Create a new access token. Deprecated; use an API key (E2B_API_KEY) instead. - */ - post: { - parameters: { - query?: never - header?: never - path?: never cookie?: never } requestBody: { content: { - 'application/json': components['schemas']['NewAccessToken'] + 'application/json': components['schemas']['NewTeamAPIKey'] } } responses: { - /** @description Access token created successfully */ + /** @description Team API key created successfully */ 201: { headers: { [name: string]: unknown } content: { - 'application/json': components['schemas']['CreatedAccessToken'] + 'application/json': components['schemas']['CreatedTeamAPIKey'] } } + 400: components['responses']['400'] 401: components['responses']['401'] - 410: components['responses']['410'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2168,7 +2143,7 @@ export interface paths { patch?: never trace?: never } - '/access-tokens/{accessTokenID}': { + '/admin/teams/{teamID}/api-keys/{apiKeyID}': { parameters: { query?: never header?: never @@ -2179,29 +2154,33 @@ export interface paths { put?: never post?: never /** - * Delete access token - * @description Delete an access token + * Delete team API key as admin + * @description Deletes a team API key for internal service workflows. */ delete: { parameters: { query?: never header?: never path: { - accessTokenID: components['parameters']['accessTokenID'] + /** @description Team ID */ + teamID: string + apiKeyID: components['parameters']['apiKeyID'] } cookie?: never } requestBody?: never responses: { - /** @description Access token deleted successfully */ + /** @description Team API key deleted successfully */ 204: { headers: { [name: string]: unknown } content?: never } + 400: components['responses']['400'] 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2240,6 +2219,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2271,6 +2251,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2314,6 +2295,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2347,6 +2329,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2382,6 +2365,7 @@ export interface paths { } } 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2414,6 +2398,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2456,6 +2441,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2485,6 +2471,7 @@ export interface paths { } 401: components['responses']['401'] 404: components['responses']['404'] + 429: components['responses']['429'] 500: components['responses']['500'] } } @@ -2493,56 +2480,949 @@ export interface paths { patch?: never trace?: never } -} -export type webhooks = Record -export interface components { - schemas: { - Team: { - /** @description Identifier of the team */ - teamID: string - /** @description Name of the team */ - name: string - /** @description API key for the team */ - apiKey: string - /** @description Whether the team is the default team */ - isDefault: boolean + '/secrets': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never } - TeamUser: { - /** - * Format: uuid - * @description Identifier of the user - */ - id: string - /** - * @deprecated - * @description Email of the user - * @default null - */ - email: string | null + /** + * List project secrets + * @description List the project's secrets. No response carries a secret value. + */ + get: { + parameters: { + query?: { + /** @description Cursor to start the list from */ + nextToken?: components['parameters']['paginationNextToken'] + /** @description Maximum number of items to return per page */ + limit?: components['parameters']['paginationLimit'] + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully listed the project's secrets */ + 200: { + headers: { + 'X-Next-Token': components['headers']['XNextToken'] + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } } - TemplateUpdateRequest: { - /** @description Whether the template is public or only accessible by the team */ - public?: boolean + put?: never + /** + * Create a secret + * @description Create a secret by storing a runtime marker as its first version. The response carries metadata only. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['NewSecret'] + } + } + responses: { + /** @description Successfully created the secret */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } } - TemplateUpdateResponse: { - /** @description Names of the template (namespace/alias format when namespaced) */ - names: string[] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/secrets/{secretID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never } /** - * Format: int32 - * @description CPU cores for the sandbox + * Get a secret + * @description Get one secret's metadata, selected by identifier or name. */ - CPUCount: number + get: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully retrieved the secret */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } + put?: never /** - * Format: int32 - * @description Memory for the sandbox in MiB + * Update a secret + * @description Replace the secret's stored marker by appending a new version. The response carries metadata only. */ - MemoryMB: number + post: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['SecretUpdate'] + } + } + responses: { + /** @description Successfully updated the secret */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Secret'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } /** - * Format: int32 - * @description Disk size for the sandbox in MiB + * Delete a secret + * @description Revoke the secret and schedule its versions for cleanup. + */ + delete: { + parameters: { + query?: never + header?: never + path: { + secretID: components['parameters']['secretID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully deleted the secret */ + 204: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 502: components['responses']['502'] + 504: components['responses']['504'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List rigs of a cluster + * @description List the orchestrator node pools ("rigs") of a cluster with a snapshot of their scaling groups. Forwarded to the cluster's edge service; a cluster with no rig management configured returns an empty list, and the local cluster answers 501. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the rigs of the cluster */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Rig'][] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/capacity': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + /** + * Set the capacity of a rig + * @description Set the desired instance count on the rig's scaling group. The value is passed to the cloud provider unchanged; violations of the group's bounds or conflicting concurrent operations surface as errors. + */ + put: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['RigCapacityChange'] + } + } + responses: { + /** @description Capacity change accepted */ + 202: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/instances/{instanceID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + post?: never + /** + * Terminate an instance of a rig + * @description Terminate an instance in whichever rig's scaling group it belongs to. The caller chooses whether the rig shrinks or the instance is replaced. + */ + delete: { + parameters: { + query: { + /** @description When true, desired capacity is decremented (rig shrinks); when false, the scaling group launches a replacement instance */ + decrementDesired: boolean + } + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Provider instance ID */ + instanceID: string + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Instance termination accepted */ + 202: { + headers: { + [name: string]: unknown + } + content?: never + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 409: components['responses']['409'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/instances': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List the instances attached to a rig + * @description List the instances attached to the rig's scaling group with their creation time and transition state, sorted by instance ID. + */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the instances of the rig */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['RigInstance'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/clusters/{clusterID}/rigs/{rigID}/errors': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List recent scaling errors of a rig + * @description List recent scaling errors on the rig's scaling group (e.g. failed instance creations due to resource exhaustion), newest first. + */ + get: { + parameters: { + query?: { + /** @description Maximum number of errors to return */ + limit?: number + } + header?: never + path: { + /** @description Identifier of the cluster */ + clusterID: components['parameters']['clusterID'] + /** @description Rig identifier (e.g. "default") */ + rigID: components['parameters']['rigID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the scaling errors of the rig */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['RigError'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 429: components['responses']['429'] + 500: components['responses']['500'] + 501: components['responses']['501'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/sandboxes/{sandboxID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get sandbox events */ + get: { + parameters: { + query?: { + offset?: number + limit?: number + orderAsc?: boolean + /** @description Filter events to the provided event types */ + types?: string[] + } + header?: never + path: { + sandboxID: components['parameters']['sandboxID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the sandbox events */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SandboxEvent'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/sandboxes': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get all sandbox events for the team associated with the API key */ + get: { + parameters: { + query?: { + offset?: number + limit?: number + orderAsc?: boolean + /** @description Filter events to the provided event types */ + types?: string[] + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the sandbox events */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['SandboxEvent'][] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description List registered webhooks. */ + get: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description List of registered webhooks. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'][] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + /** @description Register events webhook. */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['WebhookCreate'] + } + } + responses: { + /** @description Successfully created webhook. */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookCreation'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks/{webhookID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get a registered webhook. */ + get: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned the webhook configuration. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + /** @description Delete a registered webhook. */ + delete: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully deleted webhook. */ + 200: { + headers: { + [name: string]: unknown + } + content?: never + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + options?: never + head?: never + /** @description Update a registered webhook configuration. */ + patch: { + parameters: { + query?: never + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['WebhookConfiguration'] + } + } + responses: { + /** @description Successfully updated webhook. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDetail'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + trace?: never + } + '/events/webhooks/{webhookID}/deliveries': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description List webhook delivery attempts. */ + get: { + parameters: { + query?: { + /** @description Opaque cursor from the previous response's nextCursor field. */ + cursor?: string + limit?: number + orderAsc?: boolean + /** @description Include deliveries at or after this timestamp. */ + start?: string + /** @description Include deliveries before this timestamp. */ + end?: string + /** @description Filter deliveries by delivery status */ + deliveryStatus?: ('success' | 'failed')[] + /** @description Filter deliveries by event type */ + eventType?: string[] + } + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description List of webhook delivery attempts grouped by event. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDeliveriesListPayload'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/events/webhooks/{webhookID}/stats': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** @description Get webhook delivery aggregate stats. */ + get: { + parameters: { + query?: { + /** @description Inclusive stats range start. Defaults to 24 hours ago. */ + start?: string + /** @description Exclusive stats range end. Defaults to now. */ + end?: string + } + header?: never + path: { + webhookID: components['parameters']['webhookID'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Webhook delivery stats. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['WebhookDeliveryStats'] + } + } + 401: components['responses']['401'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } +} +export type webhooks = Record +export interface components { + schemas: { + /** @description An orchestrator node pool backed by one cloud scaling group */ + Rig: { + /** @description Rig identifier (e.g. "default") */ + id: string + /** @description Cloud provider backing the rig ("aws" or "gcp") */ + provider: string + /** @description Canonical cloud resource ID of the scaling group backing the rig (ARN on AWS, self-link on GCP) */ + resourceID: string + /** + * Format: int32 + * @description Desired number of instances in the rig + */ + capacityDesired: number + /** + * Format: int32 + * @description Minimum capacity enforced on the rig's scaling group. Omitted when nothing enforces bounds (GCP MIG without an active autoscaler). + */ + capacityMin?: number + /** + * Format: int32 + * @description Maximum capacity enforced on the rig's scaling group. Omitted when nothing enforces bounds (GCP MIG without an active autoscaler). + */ + capacityMax?: number + /** + * Format: int32 + * @description Number of instances currently attached to the rig + */ + capacityCurrent: number + } + /** @description Desired capacity to set on the rig's scaling group */ + RigCapacityChange: { + /** + * Format: int32 + * @description Absolute desired number of instances in the rig + */ + desired: number + } + /** @description An instance attached to a rig's scaling group */ + RigInstance: { + /** @description Provider instance ID (EC2 instance ID on AWS, instance name on GCP), also the node ID the orchestrator reports */ + id: string + /** + * Format: date-time + * @description When the provider created the instance. Omitted while the instance is transitioning. + */ + createdAt?: string + /** @description The provider is creating, deleting, recreating or otherwise mutating the instance */ + transitioning: boolean + /** @description The instance is on its way out of the group and can never become healthy again */ + terminating: boolean + } + /** @description Scaling error on the rig's scaling group, e.g. a failed instance creation due to resource exhaustion */ + RigError: { + /** + * Format: date-time + * @description When the error occurred + */ + timestamp: string + /** @description Provider-specific error code (e.g. ZONE_RESOURCE_POOL_EXHAUSTED, Failed) */ + code: string + /** @description Human-readable error message */ + message: string + /** @description Instance the error relates to, if any */ + instance?: string + /** @description Action being performed when the error occurred (e.g. CREATING) */ + action?: string + } + Team: { + /** @description Identifier of the team */ + teamID: string + /** @description Name of the team */ + name: string + /** @description API key for the team */ + apiKey: string + /** @description Whether the team is the default team */ + isDefault: boolean + } + TeamUser: { + /** + * Format: uuid + * @description Identifier of the user + */ + id: string + /** + * @deprecated + * @description Email of the user + * @default null + */ + email: string | null + } + TemplateUpdateRequest: { + /** @description Whether the template is public or only accessible by the team */ + public?: boolean + } + TemplateUpdateResponse: { + /** @description Names of the template (namespace/alias format when namespaced) */ + names: string[] + } + /** + * Format: int32 + * @description CPU cores for the sandbox + */ + CPUCount: number + /** + * Format: int32 + * @description Memory for the sandbox in MiB + */ + MemoryMB: number + /** + * Format: int32 + * @description Disk size for the sandbox in MiB + */ + DiskSizeMB: number + /** + * Format: int32 + * @description Requested minimum free space after the template's build steps, in MiB. Omit to use the team's default. Set to 0 to request no minimum free-disk growth. The filesystem is never shrunk, including inherited or already-larger filesystems. Growth is best effort, so filesystem metadata can leave the available space slightly below the requested minimum. */ - DiskSizeMB: number + MinFreeDiskMb: number /** @description Version of the envd running in the sandbox */ EnvdVersion: string SandboxMetadata: { @@ -2553,6 +3433,12 @@ export interface components { * @enum {string} */ SandboxState: 'running' | 'paused' + /** + * @description Sort direction + * @default desc + * @enum {string} + */ + OrderDirection: 'asc' | 'desc' SnapshotInfo: { /** @description Identifier of the snapshot template including the tag. Uses namespace/alias when a name was provided (e.g. team-slug/my-snapshot:default), otherwise falls back to the raw template ID (e.g. abc123:default). */ snapshotID: string @@ -2579,7 +3465,9 @@ export interface components { egressProxy?: components['schemas']['SandboxEgressProxyConfig'] /** @description Specify host mask which will be used for all sandbox requests */ maskRequestHost?: string - /** @description Per-domain transform rules applied to matching egress HTTP/HTTPS requests. Keys are domains (e.g. "api.example.com", "example.com"). A domain listed here is not automatically allowed - use allowOut to permit the traffic. */ + /** @description Sandbox ports that serve HTTPS rather than plaintext HTTP. Affects how the proxy reaches the service inside the sandbox; the public URL is HTTPS either way. Certificates are not verified, so self-signed ones work. The envd port (49983) cannot be listed. */ + httpsPorts?: number[] + /** @description Per-domain transform rules applied to matching outbound HTTPS requests. Keys may be exact DNS names (for example, "api.example.com") or a leading wildcard (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take precedence, followed by the longest matching wildcard suffix, and matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may expose transformed credentials to every matching destination the sandbox contacts. Rules do not grant network access; configure allowOut separately to permit the destination. */ rules?: { [key: string]: components['schemas']['SandboxNetworkRule'][] } @@ -2591,7 +3479,7 @@ export interface components { /** @description List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. */ denyOut?: string[] egressProxy?: components['schemas']['SandboxEgressProxyConfig'] - /** @description Per-domain transform rules. Replaces all existing rules when provided. */ + /** @description Per-domain transform rules applied to matching outbound HTTPS requests. Replaces all existing rules when provided. Keys may be exact DNS names or a single leading wildcard (for example, "*.example.com"), and are normalized to lowercase on write. Wildcards match subdomains at any depth but not the apex domain; a bare "*" is invalid. Exact rules take precedence, followed by the longest matching wildcard suffix, and matching rule sets are not merged. Broad wildcards such as "*.com" are allowed and may expose transformed credentials to every matching destination the sandbox contacts. Rules do not grant network access; configure allowOut separately to permit the destination. */ rules?: { [key: string]: components['schemas']['SandboxNetworkRule'][] } @@ -2853,6 +3741,36 @@ export interface components { iam?: components['schemas']['SandboxIam'] volumeMounts?: components['schemas']['SandboxVolumeMount'][] } + /** @description Sandbox creation request. All system communication with the sandbox is always secured; the template's envd version must support secured access. */ + NewSandboxV2: { + /** @description Identifier of the required template */ + templateID: string + /** + * Format: int32 + * @description Time to live for the sandbox in seconds. + * @default 300 + */ + timeout: number + /** + * @description Automatically pauses the sandbox after the timeout + * @default false + */ + autoPause: boolean + /** + * @description Controls the snapshot kind taken when the sandbox auto-pauses on timeout (only relevant when autoPause is true). When false, the auto-pause drops the in-memory state and persists only the filesystem (a filesystem-only snapshot); resuming it cold-boots (reboots) the sandbox from disk. Such a snapshot cannot be auto-resumed by traffic and must be resumed explicitly, so it cannot be combined with autoResume. Defaults to true (full memory snapshot). + * @default true + */ + autoPauseMemory: boolean + autoResume?: components['schemas']['SandboxAutoResumeConfig'] + /** @description Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut to 0.0.0.0/0 in the network config. */ + allow_internet_access?: boolean + network?: components['schemas']['SandboxNetworkConfig'] + metadata?: components['schemas']['SandboxMetadata'] + envVars?: components['schemas']['EnvVars'] + mcp?: components['schemas']['Mcp'] + iam?: components['schemas']['SandboxIam'] + volumeMounts?: components['schemas']['SandboxVolumeMount'][] + } /** @description Sandbox workload identity configuration. A non-empty, valid tokens map enables workload identity for the sandbox. */ SandboxIam: { tokens?: components['schemas']['SandboxIamTokens'] @@ -2879,6 +3797,8 @@ export interface components { * @description Automatically pauses the sandbox after the timeout */ autoPause?: boolean + /** @description Defaults to true. When false, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean } ConnectSandbox: { /** @@ -2886,6 +3806,18 @@ export interface components { * @description Timeout in seconds from the current time after which the sandbox should expire */ timeout: number + /** @description Defaults to true. When false and the sandbox is paused, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean + } + ConnectSandboxV2: { + /** + * Format: int32 + * @description Timeout in seconds from the current time after which the sandbox should expire + * @default 300 + */ + timeout: number + /** @description Defaults to true. When false and the sandbox is paused, resume from disk state only: the sandbox cold-boots fresh and any memory in the snapshot is ignored, never modified or deleted. Disk state has crash-recovery semantics — writes not flushed before the pause may be lost. A no-op for snapshots that contain no memory. Rejected with an error in environments where this capability is not enabled, never silently downgraded to a memory restore. */ + memory?: boolean } SandboxTimeoutRequest: { /** @@ -2974,6 +3906,14 @@ export interface components { /** @description Number of sandboxes that failed to kill */ failedCount: number } + /** + * @description Cached live sandbox index count keyed by team ID. Counts may briefly + * include sandboxes transitioning out of running; teams without indexed + * sandboxes are omitted. + */ + AdminTeamRunningSandboxCounts: { + [key: string]: number + } AdminBuildCancelResult: { /** @description Number of builds successfully cancelled */ cancelledCount: number @@ -3046,46 +3986,6 @@ export interface components { */ aliases: string[] } - TemplateLegacy: { - /** @description Identifier of the template */ - templateID: string - /** @description Identifier of the last successful build for given template */ - buildID: string - cpuCount: components['schemas']['CPUCount'] - memoryMB: components['schemas']['MemoryMB'] - diskSizeMB: components['schemas']['DiskSizeMB'] - /** @description Whether the template is public or only accessible by the team */ - public: boolean - /** @description Aliases of the template */ - aliases: string[] - /** - * Format: date-time - * @description Time when the template was created - */ - createdAt: string - /** - * Format: date-time - * @description Time when the template was last updated - */ - updatedAt: string - createdBy: components['schemas']['TeamUser'] | null - /** - * Format: date-time - * @description Time when the template was last used - */ - lastSpawnedAt: string | null - /** - * Format: int64 - * @description Number of times the template was used - */ - spawnCount: number - /** - * Format: int32 - * @description Number of times the template was built - */ - buildCount: number - envdVersion: components['schemas']['EnvdVersion'] - } TemplateBuild: { /** * Format: uuid @@ -3154,20 +4054,6 @@ export interface components { /** @description Whether the template is public or only accessible by the team */ public: boolean } - TemplateBuildRequest: { - /** @description Alias of the template */ - alias?: string - /** @description Dockerfile for the template */ - dockerfile: string - /** @description Identifier of the team */ - teamID?: string - /** @description Start command to execute in the template after the build */ - startCmd?: string - /** @description Ready check command to execute in the template after the build */ - readyCmd?: string - cpuCount?: components['schemas']['CPUCount'] - memoryMB?: components['schemas']['MemoryMB'] - } /** @description Step in the template build process */ TemplateStep: { /** @description Type of the step */ @@ -3202,17 +4088,7 @@ export interface components { teamID?: string cpuCount?: components['schemas']['CPUCount'] memoryMB?: components['schemas']['MemoryMB'] - } - TemplateBuildRequestV2: { - /** @description Alias of the template */ - alias: string - /** - * @deprecated - * @description Identifier of the team - */ - teamID?: string - cpuCount?: components['schemas']['CPUCount'] - memoryMB?: components['schemas']['MemoryMB'] + minFreeDiskMb?: components['schemas']['MinFreeDiskMb'] } FromImageRegistry: | components['schemas']['AWSRegistry'] @@ -3251,6 +4127,7 @@ export interface components { /** @description Password to use for the registry */ password: string } + /** @description Exactly one of fromImage or fromTemplate must be given and non-empty. */ TemplateBuildStartV2: { /** @description Image to use as a base for the template build */ fromImage?: string @@ -3277,6 +4154,10 @@ export interface components { present: boolean /** @description Url where the file should be uploaded to */ url?: string + /** @description Request headers that must be sent with the upload request */ + headers?: { + [key: string]: string + } } /** * @description State of the sandbox @@ -3313,7 +4194,8 @@ export interface components { TemplateBuildStatus: 'building' | 'waiting' | 'ready' | 'error' TemplateBuildInfo: { /** - * @description Build logs + * @deprecated + * @description Build logs (always empty since the V1 build path was removed, use logEntries) * @default [] */ logs: string[] @@ -3348,11 +4230,16 @@ export interface components { LogsSource: 'temporary' | 'persistent' /** * @description Status of the node. - * - draining: the node is bound to be shut down. It will not accept new sandboxes and will stop once all existing sandboxes are done. * - standby: the node is not actively used, but it can return to ready and continue serving traffic. * @enum {string} */ - NodeStatus: 'ready' | 'draining' | 'connecting' | 'unhealthy' | 'standby' + NodeStatus: + | 'ready' + | 'draining' + | 'connecting' + | 'unhealthy' + | 'standby' + | 'shutting_down' NodeStatusChange: { /** * Format: uuid @@ -3467,6 +4354,16 @@ export interface components { * @description Number of sandboxes running on the node */ sandboxCount: number + /** + * Format: int64 + * @description Node-scoped configured sandbox admission limit. Nonpositive values reject creation. Omitted when unknown or not an orchestrator. + */ + maxSandboxes?: number + /** + * Format: uint64 + * @description Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. + */ + outstandingWork: number metrics: components['schemas']['NodeMetrics'] /** * Format: uint64 @@ -3507,9 +4404,17 @@ export interface components { * @description Number of sandboxes running on the node */ sandboxCount: number + /** + * Format: int64 + * @description Node-scoped configured sandbox admission limit. Nonpositive values reject creation. Omitted when unknown or not an orchestrator. + */ + maxSandboxes?: number + /** + * Format: uint64 + * @description Cached count of work holds on the node. Zero means idle or not yet reported; it does not by itself authorize deletion. + */ + outstandingWork: number metrics: components['schemas']['NodeMetrics'] - /** @description List of cached builds id on the node */ - cachedBuilds: string[] /** * Format: uint64 * @description Number of sandbox create successes @@ -3521,27 +4426,6 @@ export interface components { */ createFails: number } - CreatedAccessToken: { - /** - * Format: uuid - * @description Identifier of the access token - */ - id: string - /** @description Name of the access token */ - name: string - /** @description The fully created access token */ - token: string - mask: components['schemas']['IdentifierMaskingDetails'] - /** - * Format: date-time - * @description Timestamp of access token creation - */ - createdAt: string - } - NewAccessToken: { - /** @description Name of the access token */ - name: string - } TeamAPIKey: { /** * Format: uuid @@ -3635,6 +4519,8 @@ export interface components { * @description Error code */ code: number + /** @description Machine-readable semantic error code. Not a closed set; initial values: sandbox_capacity_unavailable, sandbox_placement_timeout, sandbox_no_compatible_node, sandbox_create_failed, internal_server_error. */ + error_code?: string /** @description Error */ message: string } @@ -3661,11 +4547,286 @@ export interface components { name: string /** @description Auth token to use for interacting with volume content */ token: string + /** + * @description Domain to use as the destination for volume content requests, + * replacing the default `api.`. Only returned when the + * team is connected to a custom (BYOC) cluster; absent otherwise, in + * which case the default domain is used. + */ + domain?: string } NewVolume: { /** @description Name of the volume */ name: string } + /** @description Customer metadata of the secret. Always present, empty when unset. At most 32 entries; keys are limited to 128 bytes, values to 1024 bytes, and a secret's metadata to 8192 bytes in total. */ + SecretMetadata: { + [key: string]: string + } + /** @description Metadata of a secret. It never carries the secret value. */ + Secret: { + /** @description Identifier of the secret */ + secretID: string + /** @description Name of the secret, unique within the project */ + name: string + /** + * Format: int64 + * @description Version served to readers that do not name one + */ + currentVersion: number + metadata: components['schemas']['SecretMetadata'] + /** + * Format: date-time + * @description Time when the secret was created + */ + createdAt: string + /** + * Format: date-time + * @description Time when the secret was last updated + */ + updatedAt: string + } + NewSecret: { + /** @description Name of the secret, unique within the project. Names are lower-cased before storage and returned in that canonical form; the sec_ prefix is reserved for secret identifiers. */ + name: string + /** @description Runtime marker stored as the secret's first version. The runtime resolves it to a value at sandbox egress. */ + value: string + metadata?: components['schemas']['SecretMetadata'] + } + SecretUpdate: { + /** @description Runtime marker stored as the secret's new version. The runtime resolves it to a value at sandbox egress. */ + value: string + metadata?: components['schemas']['SecretMetadata'] + } + /** @description Sandbox event */ + SandboxEvent: { + /** + * Format: uuid + * @description Event unique identifier + */ + id: string + /** @description Event structure version */ + version: string + /** @description Event name */ + type: string + /** + * @deprecated + * @description Category of the event (e.g., 'lifecycle', 'process', etc.) + */ + eventCategory?: string + /** + * @deprecated + * @description Label for the specific event type (e.g., 'sandbox_started', 'process_oom', etc.) + */ + eventLabel?: string + /** @description Optional JSON data associated with the event */ + eventData?: Record | null + /** + * Format: date-time + * @description Timestamp of the event + */ + timestamp: string + /** + * Format: string + * @description Unique identifier for the sandbox + */ + sandboxId: string + /** + * Format: string + * @description Unique identifier for the sandbox execution + */ + sandboxExecutionId: string + /** + * Format: string + * @description Unique identifier for the sandbox template + */ + sandboxTemplateId: string + /** + * Format: string + * @description Unique identifier for the sandbox build + */ + sandboxBuildId: string + /** + * Format: uuid + * @description Team identifier associated with the sandbox + */ + sandboxTeamId: string + } + /** @description Configuration for registering new webhooks */ + WebhookCreate: { + name: string + /** Format: uri */ + url: string + events: string[] + /** @default true */ + enabled: boolean + /** @description Secret used to sign the webhook payloads */ + signatureSecret: string + } + /** @description Webhook creation response */ + WebhookCreation: { + /** @description Webhook unique identifier */ + id: string + /** @description Webhook user friendly name */ + name: string + /** + * Format: date-time + * @description Time when the template was created + */ + createdAt: string + /** @description Unique identifier for the team */ + teamId: string + /** Format: uri */ + url: string + enabled: boolean + events: string[] + } + /** @description Webhook detail response */ + WebhookDetail: { + /** @description Webhook unique identifier */ + id: string + /** @description Unique identifier for the team */ + teamId: string + /** @description Webhook user friendly name */ + name: string + /** + * Format: date-time + * @description Time when the template was created + */ + createdAt: string + /** Format: uri */ + url: string + enabled: boolean + events: string[] + } + /** @description Configuration for updating existing webhooks */ + WebhookConfiguration: { + enabled?: boolean + /** @description Webhook user friendly name */ + name?: string + /** Format: uri */ + url?: string + events?: string[] + /** @description Secret used to sign the webhook payloads */ + signatureSecret?: string + } + /** @description Webhook delivery attempt */ + WebhookDelivery: { + /** + * Format: uuid + * @description Delivery attempt identifier + */ + id: string + /** + * Format: uuid + * @description Team identifier + */ + teamId: string + /** + * Format: uuid + * @description Webhook configuration identifier + */ + webhookId: string + /** + * Format: uuid + * @description Sandbox event identifier + */ + eventId: string + /** @description Sandbox identifier */ + sandboxId: string + /** @description Sandbox event type */ + eventType: string + /** + * @description Delivery attempt status + * @enum {string} + */ + status: 'success' | 'failed' + /** + * Format: int32 + * @description Delivery request duration in milliseconds + */ + durationMs: number + /** @description Serialized webhook request body */ + requestBody: string + /** @description JSON-encoded request headers with sensitive values redacted */ + requestHeaders: string + /** + * Format: uri + * @description URL attempted for this delivery + */ + requestUrl: string + /** @description Truncated response body, if a response was received */ + responseBody?: string | null + /** @description JSON-encoded response headers, if a response was received */ + responseHeaders?: string | null + /** + * Format: int32 + * @description HTTP response status code, if a response was received + */ + responseHttpStatusCode?: number | null + /** + * @description Machine-readable non-HTTP or HTTP failure class + * @enum {string|null} + */ + errorClass: + | 'http_error' + | 'dns_error' + | 'timeout' + | 'transport_error' + | 'request_error' + | 'signature_error' + | 'canceled' + | null + /** @description Error message for failures without a useful response body */ + errorMessage?: string | null + /** + * Format: date-time + * @description Time when the delivery attempt started + */ + timestamp: string + } + /** @description Webhook delivery aggregate stats */ + WebhookDeliveryStats: { + buckets: components['schemas']['WebhookDeliveryStatsBucket'][] + /** Format: int64 */ + total: number + /** Format: int64 */ + failed: number + durationMs: components['schemas']['WebhookDeliveryDurationStats'] + } + /** @description Webhook delivery duration statistics in milliseconds */ + WebhookDeliveryDurationStats: { + /** Format: double */ + minimum: number + /** Format: double */ + average: number + /** Format: double */ + maximum: number + } + /** @description Webhook delivery stats for a time bucket */ + WebhookDeliveryStatsBucket: { + /** Format: date-time */ + timestamp: string + /** Format: int64 */ + total: number + /** Format: int64 */ + failed: number + durationMs: components['schemas']['WebhookDeliveryDurationStats'] + } + /** @description Webhook delivery attempts grouped by sandbox event */ + WebhookDeliveryGroup: { + /** Format: uuid */ + eventId: string + eventType: string + sandboxId: string + attempts: components['schemas']['WebhookDelivery'][] + } + /** @description Paginated webhook delivery attempts grouped by event */ + WebhookDeliveriesListPayload: { + data: components['schemas']['WebhookDeliveryGroup'][] + /** @description Cursor to pass to the next list request, or null when there is no next page. */ + nextCursor: string | null + } } responses: { /** @description Bad request */ @@ -3713,9 +4874,14 @@ export interface components { 'application/json': components['schemas']['Error'] } } - /** @description Gone */ - 410: { + /** @description Too many requests */ + 429: { headers: { + /** + * @description When present, the number of seconds to wait before retrying the request. + * @example 30 + */ + 'Retry-After'?: number [name: string]: unknown } content: { @@ -3731,15 +4897,54 @@ export interface components { 'application/json': components['schemas']['Error'] } } + /** @description Not implemented by this deployment */ + 501: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Backend error */ + 502: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Service unavailable */ + 503: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } + /** @description Backend timeout */ + 504: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } } parameters: { + /** @description Identifier of the cluster */ + clusterID: string + /** @description Rig identifier (e.g. "default") */ + rigID: string templateID: string buildID: string sandboxID: string teamID: string nodeID: string apiKeyID: string - accessTokenID: string snapshotID: string tag: string /** @description Maximum number of items to return per page */ @@ -3747,9 +4952,16 @@ export interface components { /** @description Cursor to start the list from */ paginationNextToken: string volumeID: string + secretID: string + webhookID: string } requestBodies: never - headers: never + headers: { + /** @description Cursor to fetch the next page of results, if more exist */ + XNextToken: string + /** @description Number of running sandboxes matching the filters, before pagination is applied. Only present when running sandboxes were requested. */ + XTotalRunning: number + } pathItems: never } export type $defs = Record