diff --git a/spec/openapi.dashboard-api.yaml b/spec/openapi.dashboard-api.yaml index 453c94c8b..7d1095207 100644 --- a/spec/openapi.dashboard-api.yaml +++ b/spec/openapi.dashboard-api.yaml @@ -10,6 +10,9 @@ components: type: apiKey in: header name: X-Admin-Token + # Generated code uses security schemas in the alphabetical order. + # In order to check first the token, and then the team (so we can already use the user), + # there is a 1 and 2 present in the names of the security schemas. Supabase1TokenAuth: type: apiKey in: header @@ -18,6 +21,16 @@ components: type: apiKey in: header name: X-Supabase-Team + # AuthProviderBearerAuth / AuthProviderTeamAuth: B before T in the name + # so Bearer is validated before Team (same reason as Supabase1/2 above). + AuthProviderBearerAuth: + type: http + scheme: bearer + bearerFormat: access_token + AuthProviderTeamAuth: + type: apiKey + in: header + name: X-Team-ID parameters: build_id: @@ -108,6 +121,77 @@ components: description: Team slug to resolve. schema: type: string + templates_limit: + name: limit + in: query + required: false + description: Maximum number of items to return per page. + schema: + type: integer + format: int32 + minimum: 1 + maximum: 100 + default: 50 + templates_cursor: + name: cursor + in: query + required: false + description: Cursor returned by the previous list response in `{sort}|{value}|{templateID}` format. Rejected if its sort does not match the request. + schema: + type: string + templates_cpu_count: + name: cpuCount + in: query + required: false + description: Filter templates by exact vCPU count. + schema: + type: integer + format: int32 + minimum: 1 + templates_memory_mb: + name: memoryMB + in: query + required: false + description: Filter templates by exact memory size in MB. + schema: + type: integer + format: int32 + minimum: 1 + templates_public: + name: public + in: query + required: false + description: Filter templates by visibility (true = public, false = internal). + schema: + type: boolean + templates_search: + name: search + in: query + required: false + description: Case-insensitive substring match on template names, aliases, and template id. + schema: + type: string + templates_sort: + name: sort + in: query + required: false + description: Sort column and direction. + schema: + type: string + enum: + [ + name_asc, + name_desc, + cpu_count_asc, + cpu_count_desc, + memory_mb_asc, + memory_mb_desc, + created_at_asc, + created_at_desc, + updated_at_asc, + updated_at_desc, + ] + default: created_at_desc responses: "400": @@ -140,6 +224,12 @@ components: application/json: schema: $ref: "#/components/schemas/Error" + "502": + description: Upstream error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" schemas: Error: @@ -156,6 +246,89 @@ components: type: string description: Error message. + AdminAuthProviderProfile: + type: object + required: + - userId + - email + properties: + userId: + type: string + format: uuid + description: Internal E2B user identifier. + email: + type: string + nullable: true + description: Email address from the configured auth provider. + + AdminAuthProviderProfilesResponse: + type: object + required: + - profiles + properties: + profiles: + type: array + items: + $ref: "#/components/schemas/AdminAuthProviderProfile" + + AdminAuthProviderProfilesResolveRequest: + type: object + required: + - userIds + properties: + userIds: + type: array + minItems: 1 + maxItems: 100 + uniqueItems: true + items: + type: string + format: uuid + + AdminAuthProviderProfilesLookupEmailRequest: + type: object + required: + - email + properties: + email: + type: string + format: email + + AdminAuthProviderUserBootstrapRequest: + type: object + required: + - oidc_issuer + - oidc_user_id + - oidc_user_email + properties: + oidc_issuer: + type: string + minLength: 1 + oidc_user_id: + type: string + minLength: 1 + oidc_user_email: + type: string + format: email + oidc_user_name: + type: string + nullable: true + + AdminTeamBootstrapRequest: + type: object + required: + - name + - email + properties: + name: + type: string + minLength: 1 + description: Team name. + email: + type: string + format: email + description: Billing/contact email for the team. + BuildStatus: type: string description: Build status mapped for dashboard clients. @@ -438,12 +611,24 @@ components: - email - isDefault - createdAt + - providers properties: id: type: string format: uuid email: type: string + name: + type: string + nullable: true + profilePictureUrl: + type: string + format: uri + nullable: true + providers: + type: array + items: + type: string isDefault: type: boolean addedBy: @@ -580,6 +765,103 @@ components: items: $ref: "#/components/schemas/DefaultTemplate" + TeamTemplate: + type: object + required: + - templateID + - buildID + - cpuCount + - memoryMB + - diskSizeMB + - public + - aliases + - names + - createdAt + - updatedAt + - createdBy + - lastSpawnedAt + - spawnCount + - buildCount + - envdVersion + - isDefault + properties: + templateID: + type: string + buildID: + type: string + format: uuid + cpuCount: + type: integer + format: int64 + memoryMB: + type: integer + format: int64 + diskSizeMB: + type: integer + format: int64 + nullable: true + public: + type: boolean + aliases: + type: array + items: + type: string + names: + type: array + items: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + createdBy: + nullable: true + type: object + required: + - id + properties: + id: + type: string + format: uuid + email: + type: string + nullable: true + lastSpawnedAt: + type: string + format: date-time + nullable: true + spawnCount: + type: integer + format: int64 + buildCount: + type: integer + format: int32 + envdVersion: + type: string + nullable: true + isDefault: + type: boolean + defaultDescription: + type: string + nullable: true + + TeamTemplatesResponse: + type: object + required: + - data + - nextCursor + properties: + data: + type: array + items: + $ref: "#/components/schemas/TeamTemplate" + nextCursor: + type: string + nullable: true + description: Cursor to pass to the next list request, or `null` if there is no next page. + TeamResolveResponse: type: object required: @@ -617,6 +899,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/build_id_or_template" - $ref: "#/components/parameters/build_statuses" @@ -645,6 +929,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/build_ids" @@ -671,6 +957,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/build_id" responses: @@ -696,6 +984,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -721,6 +1011,7 @@ paths: tags: [teams] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] responses: "200": description: Successfully returned user teams. @@ -737,6 +1028,7 @@ paths: tags: [teams] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] requestBody: required: true content: @@ -777,6 +1069,135 @@ paths: "500": $ref: "#/components/responses/500" + /admin/users/bootstrap: + post: + summary: Bootstrap auth provider user + tags: [teams] + security: + - AdminTokenAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderUserBootstrapRequest" + responses: + "200": + description: Successfully bootstrapped user. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamResolveResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /admin/teams/bootstrap: + post: + summary: Bootstrap team + description: Creates and provisions a team for an admin-authenticated bootstrap workflow. + tags: [admin] + security: + - AdminTokenAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminTeamBootstrapRequest" + responses: + "200": + description: Successfully bootstrapped team. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamResolveResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "502": + $ref: "#/components/responses/502" + "500": + $ref: "#/components/responses/500" + + /admin/user-profiles/resolve: + post: + summary: Resolve user profiles + tags: [admin] + security: + - AdminTokenAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderProfilesResolveRequest" + responses: + "200": + description: Successfully resolved profiles. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderProfilesResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /admin/user-profiles/by-email: + post: + summary: Lookup user profiles by email + tags: [admin] + security: + - AdminTokenAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderProfilesLookupEmailRequest" + responses: + "200": + description: Successfully found matching profiles. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderProfilesResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + + /admin/user-profiles/{userId}: + get: + summary: Get user profile + tags: [admin] + security: + - AdminTokenAuth: [] + parameters: + - $ref: "#/components/parameters/userId" + responses: + "200": + description: Successfully found profile. + content: + application/json: + schema: + $ref: "#/components/schemas/AdminAuthProviderProfilesResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "500": + $ref: "#/components/responses/500" + /teams/resolve: get: summary: Resolve team identity @@ -784,6 +1205,7 @@ paths: tags: [teams] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] parameters: - $ref: "#/components/parameters/teamSlug" responses: @@ -809,6 +1231,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" requestBody: @@ -840,6 +1264,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" responses: @@ -861,6 +1287,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" requestBody: @@ -890,6 +1318,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" - $ref: "#/components/parameters/userId" @@ -905,6 +1335,40 @@ paths: "500": $ref: "#/components/responses/500" + /templates: + get: + summary: List team templates + description: Returns a paginated list of the team's templates (and default templates inline, unless the team is on a dedicated cluster), with filtering, search, and column sorting via keyset cursor pagination. + tags: [templates] + security: + - Supabase1TokenAuth: [] + Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] + parameters: + - $ref: "#/components/parameters/templates_cpu_count" + - $ref: "#/components/parameters/templates_memory_mb" + - $ref: "#/components/parameters/templates_public" + - $ref: "#/components/parameters/templates_search" + - $ref: "#/components/parameters/templates_sort" + - $ref: "#/components/parameters/templates_limit" + - $ref: "#/components/parameters/templates_cursor" + responses: + "200": + description: Successfully returned paginated templates. + content: + application/json: + schema: + $ref: "#/components/schemas/TeamTemplatesResponse" + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" + "500": + $ref: "#/components/responses/500" + /templates/defaults: get: summary: List default templates @@ -912,6 +1376,7 @@ paths: tags: [templates] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] responses: "200": description: Successfully returned default templates. diff --git a/spec/openapi.infra.yaml b/spec/openapi.infra.yaml index 9b7157faa..3293ca8bd 100644 --- a/spec/openapi.infra.yaml +++ b/spec/openapi.infra.yaml @@ -27,6 +27,16 @@ components: type: apiKey in: header name: X-Supabase-Team + # AuthProviderBearerAuth / AuthProviderTeamAuth: B before T in the name + # so Bearer is validated before Team (same reason as Supabase1/2 above). + AuthProviderBearerAuth: + type: http + scheme: bearer + bearerFormat: access_token + AuthProviderTeamAuth: + type: apiKey + in: header + name: X-Team-ID AdminTokenAuth: type: apiKey in: header @@ -184,6 +194,9 @@ components: description: Identifier of the user email: type: string + nullable: true + deprecated: true + default: null description: Email of the user TemplateUpdateRequest: @@ -291,14 +304,41 @@ components: additionalProperties: type: array items: - $ref: '#/components/schemas/SandboxNetworkRule' + $ref: "#/components/schemas/SandboxNetworkRule" + + SandboxNetworkUpdateConfig: + type: object + description: Network configuration update for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting a field clears it. + properties: + allowOut: + type: array + description: List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. + items: + type: string + denyOut: + type: array + description: List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. + items: + type: string + rules: + type: object + description: Per-domain transform rules. Replaces all existing rules when provided. + additionalProperties: + type: array + items: + $ref: "#/components/schemas/SandboxNetworkRule" + 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. SandboxNetworkRule: type: object description: Transform rule applied to egress requests matching a domain pattern. properties: transform: - $ref: '#/components/schemas/SandboxNetworkTransform' + $ref: "#/components/schemas/SandboxNetworkTransform" SandboxNetworkTransform: type: object @@ -696,6 +736,33 @@ components: format: int32 minimum: 0 + SandboxTimeoutRequest: + type: object + required: + - timeout + properties: + timeout: + description: Timeout in seconds from the current time after which the sandbox should expire + type: integer + format: int32 + minimum: 0 + + SandboxRefreshRequest: + type: object + properties: + duration: + description: Duration for which the sandbox should be kept alive in seconds + type: integer + maximum: 3600 # 1 hour + minimum: 0 + + SandboxSnapshotRequest: + type: object + properties: + name: + type: string + description: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. + TeamMetric: description: Team metric with timestamp required: @@ -1851,6 +1918,7 @@ paths: security: - AccessTokenAuth: [] - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] responses: "200": description: Successfully returned all teams @@ -1859,8 +1927,7 @@ paths: schema: type: array items: - allOf: - - $ref: "#/components/schemas/Team" + $ref: "#/components/schemas/Team" "401": $ref: "#/components/responses/401" "500": @@ -1874,6 +1941,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" - in: query @@ -1916,6 +1985,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/teamID" - in: query @@ -1963,6 +2034,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - name: metadata in: query @@ -1978,8 +2051,7 @@ paths: schema: type: array items: - allOf: - - $ref: "#/components/schemas/ListedSandbox" + $ref: "#/components/schemas/ListedSandbox" "401": $ref: "#/components/responses/401" "400": @@ -1993,6 +2065,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -2021,6 +2095,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - name: metadata in: query @@ -2048,8 +2124,7 @@ paths: schema: type: array items: - allOf: - - $ref: "#/components/schemas/ListedSandbox" + $ref: "#/components/schemas/ListedSandbox" "401": $ref: "#/components/responses/401" "400": @@ -2065,6 +2140,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - name: sandbox_ids in: query @@ -2100,6 +2177,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2139,6 +2218,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2195,6 +2276,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2218,6 +2301,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2238,6 +2323,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" - in: query @@ -2282,6 +2369,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2305,6 +2394,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2337,6 +2428,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2374,20 +2467,14 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] tags: [sandboxes] requestBody: content: application/json: schema: - type: object - required: - - timeout - properties: - timeout: - description: Timeout in seconds from the current time after which the sandbox should expire - type: integer - format: int32 - minimum: 0 + $ref: "#/components/schemas/SandboxTimeoutRequest" parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2407,36 +2494,15 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] tags: [sandboxes] requestBody: required: true content: application/json: schema: - type: object - properties: - allowOut: - type: array - description: List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. - items: - type: string - denyOut: - type: array - description: List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. - items: - type: string - rules: - type: object - description: Per-domain transform rules. Replaces all existing rules when provided. - additionalProperties: - type: array - items: - $ref: '#/components/schemas/SandboxNetworkRule' - 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. + $ref: "#/components/schemas/SandboxNetworkUpdateConfig" parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2458,18 +2524,14 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] tags: [sandboxes] requestBody: content: application/json: schema: - type: object - properties: - duration: - description: Duration for which the sandbox should be kept alive in seconds - type: integer - maximum: 3600 # 1 hour - minimum: 0 + $ref: "#/components/schemas/SandboxRefreshRequest" parameters: - $ref: "#/components/parameters/sandboxID" responses: @@ -2488,6 +2550,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/sandboxID" requestBody: @@ -2495,11 +2559,7 @@ paths: content: application/json: schema: - type: object - properties: - name: - type: string - description: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. + $ref: "#/components/schemas/SandboxSnapshotRequest" responses: "201": description: Snapshot created successfully @@ -2524,6 +2584,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - name: sandboxID in: query @@ -2555,6 +2617,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -2573,6 +2637,8 @@ paths: $ref: "#/components/responses/400" "401": $ref: "#/components/responses/401" + "403": + $ref: "#/components/responses/403" "500": $ref: "#/components/responses/500" @@ -2585,6 +2651,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -2615,6 +2683,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - in: path @@ -2649,6 +2719,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - in: query required: false @@ -2664,8 +2736,7 @@ paths: schema: type: array items: - allOf: - - $ref: "#/components/schemas/Template" + $ref: "#/components/schemas/Template" "401": $ref: "#/components/responses/401" "500": @@ -2678,6 +2749,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -2707,6 +2780,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/paginationNextToken" @@ -2730,6 +2805,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" requestBody: @@ -2758,6 +2835,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" responses: @@ -2776,6 +2855,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" requestBody: @@ -2803,6 +2884,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -2822,6 +2905,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -2848,6 +2933,8 @@ paths: - AccessTokenAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" requestBody: @@ -2879,6 +2966,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -2926,6 +3015,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" - $ref: "#/components/parameters/buildID" @@ -2980,6 +3071,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -3008,6 +3101,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -3034,6 +3129,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/templateID" responses: @@ -3062,6 +3159,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - name: alias in: path @@ -3091,6 +3190,14 @@ paths: tags: [admin] security: - AdminTokenAuth: [] + parameters: + - in: query + name: clusterID + description: Identifier of the cluster + required: false + schema: + type: string + format: uuid responses: "200": description: Successfully returned all nodes @@ -3099,8 +3206,7 @@ paths: schema: type: array items: - allOf: - - $ref: "#/components/schemas/Node" + $ref: "#/components/schemas/Node" "401": $ref: "#/components/responses/401" "500": @@ -3214,12 +3320,80 @@ paths: "500": $ref: "#/components/responses/500" + /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] + security: + - AdminTokenAuth: [] + 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/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" + "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: + - AdminTokenAuth: [] + parameters: + - name: teamID + in: path + required: true + schema: + type: string + format: uuid + description: Team ID + - $ref: "#/components/parameters/apiKeyID" + responses: + "204": + description: Team API key deleted successfully + "400": + $ref: "#/components/responses/400" + "401": + $ref: "#/components/responses/401" + "404": + $ref: "#/components/responses/404" + "500": + $ref: "#/components/responses/500" + /access-tokens: post: description: Create a new access token tags: [access-tokens] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] requestBody: required: true content: @@ -3244,6 +3418,7 @@ paths: tags: [access-tokens] security: - Supabase1TokenAuth: [] + - AuthProviderBearerAuth: [] parameters: - $ref: "#/components/parameters/accessTokenID" responses: @@ -3263,6 +3438,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] responses: "200": description: Successfully returned all team API keys @@ -3282,6 +3459,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -3307,6 +3486,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/apiKeyID" requestBody: @@ -3330,6 +3511,8 @@ paths: security: - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/apiKeyID" responses: @@ -3350,6 +3533,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] responses: "200": description: Successfully listed all team volumes @@ -3371,6 +3556,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] requestBody: required: true content: @@ -3399,6 +3586,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/volumeID" responses: @@ -3422,6 +3611,8 @@ paths: - ApiKeyAuth: [] - Supabase1TokenAuth: [] Supabase2TeamAuth: [] + - AuthProviderBearerAuth: [] + AuthProviderTeamAuth: [] parameters: - $ref: "#/components/parameters/volumeID" responses: diff --git a/src/core/shared/contracts/dashboard-api.types.ts b/src/core/shared/contracts/dashboard-api.types.ts index 6cf043667..4736b0ef0 100644 --- a/src/core/shared/contracts/dashboard-api.types.ts +++ b/src/core/shared/contracts/dashboard-api.types.ts @@ -327,6 +327,224 @@ export interface paths { patch?: never trace?: never } + '/admin/users/bootstrap': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Bootstrap auth provider user */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminAuthProviderUserBootstrapRequest'] + } + } + responses: { + /** @description Successfully bootstrapped user. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['TeamResolveResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/bootstrap': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Bootstrap team + * @description Creates and provisions a team for an admin-authenticated bootstrap workflow. + */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminTeamBootstrapRequest'] + } + } + responses: { + /** @description Successfully bootstrapped team. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['TeamResolveResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + 502: components['responses']['502'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/user-profiles/resolve': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Resolve user profiles */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesResolveRequest'] + } + } + responses: { + /** @description Successfully resolved profiles. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/user-profiles/by-email': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** Lookup user profiles by email */ + post: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesLookupEmailRequest'] + } + } + responses: { + /** @description Successfully found matching profiles. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/user-profiles/{userId}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Get user profile */ + get: { + parameters: { + query?: never + header?: never + path: { + /** @description Identifier of the user. */ + userId: components['parameters']['userId'] + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully found profile. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['AdminAuthProviderProfilesResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/teams/resolve': { parameters: { query?: never @@ -535,6 +753,64 @@ export interface paths { patch?: never trace?: never } + '/templates': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** + * List team templates + * @description Returns a paginated list of the team's templates (and default templates inline, unless the team is on a dedicated cluster), with filtering, search, and column sorting via keyset cursor pagination. + */ + get: { + parameters: { + query?: { + /** @description Filter templates by exact vCPU count. */ + cpuCount?: components['parameters']['templates_cpu_count'] + /** @description Filter templates by exact memory size in MB. */ + memoryMB?: components['parameters']['templates_memory_mb'] + /** @description Filter templates by visibility (true = public, false = internal). */ + public?: components['parameters']['templates_public'] + /** @description Case-insensitive substring match on template names, aliases, and template id. */ + search?: components['parameters']['templates_search'] + /** @description Sort column and direction. */ + sort?: components['parameters']['templates_sort'] + /** @description Maximum number of items to return per page. */ + limit?: components['parameters']['templates_limit'] + /** @description Cursor returned by the previous list response in `{sort}|{value}|{templateID}` format. Rejected if its sort does not match the request. */ + cursor?: components['parameters']['templates_cursor'] + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + /** @description Successfully returned paginated templates. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['TeamTemplatesResponse'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 500: components['responses']['500'] + } + } + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/templates/defaults': { parameters: { query?: never @@ -589,6 +865,41 @@ export interface components { /** @description Error message. */ message: string } + AdminAuthProviderProfile: { + /** + * Format: uuid + * @description Internal E2B user identifier. + */ + userId: string + /** @description Email address from the configured auth provider. */ + email: string | null + } + AdminAuthProviderProfilesResponse: { + profiles: components['schemas']['AdminAuthProviderProfile'][] + } + AdminAuthProviderProfilesResolveRequest: { + userIds: string[] + } + AdminAuthProviderProfilesLookupEmailRequest: { + /** Format: email */ + email: string + } + AdminAuthProviderUserBootstrapRequest: { + oidc_issuer: string + oidc_user_id: string + /** Format: email */ + oidc_user_email: string + oidc_user_name?: string | null + } + AdminTeamBootstrapRequest: { + /** @description Team name. */ + name: string + /** + * Format: email + * @description Billing/contact email for the team. + */ + email: string + } /** * @description Build status mapped for dashboard clients. * @enum {string} @@ -738,6 +1049,10 @@ export interface components { /** Format: uuid */ id: string email: string + name?: string | null + /** Format: uri */ + profilePictureUrl?: string | null + providers: string[] isDefault: boolean /** Format: uuid */ addedBy?: string | null @@ -791,6 +1106,43 @@ export interface components { DefaultTemplatesResponse: { templates: components['schemas']['DefaultTemplate'][] } + TeamTemplate: { + templateID: string + /** Format: uuid */ + buildID: string + /** Format: int64 */ + cpuCount: number + /** Format: int64 */ + memoryMB: number + /** Format: int64 */ + diskSizeMB: number | null + public: boolean + aliases: string[] + names: string[] + /** Format: date-time */ + createdAt: string + /** Format: date-time */ + updatedAt: string + createdBy: { + /** Format: uuid */ + id: string + email?: string | null + } | null + /** Format: date-time */ + lastSpawnedAt: string | null + /** Format: int64 */ + spawnCount: number + /** Format: int32 */ + buildCount: number + envdVersion: string | null + isDefault: boolean + defaultDescription?: string | null + } + TeamTemplatesResponse: { + data: components['schemas']['TeamTemplate'][] + /** @description Cursor to pass to the next list request, or `null` if there is no next page. */ + nextCursor: string | null + } TeamResolveResponse: { /** Format: uuid */ id: string @@ -843,6 +1195,15 @@ export interface components { 'application/json': components['schemas']['Error'] } } + /** @description Upstream error */ + 502: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['Error'] + } + } } parameters: { /** @description Identifier of the build. */ @@ -865,6 +1226,30 @@ export interface components { userId: string /** @description Team slug to resolve. */ teamSlug: string + /** @description Maximum number of items to return per page. */ + templates_limit: number + /** @description Cursor returned by the previous list response in `{sort}|{value}|{templateID}` format. Rejected if its sort does not match the request. */ + templates_cursor: string + /** @description Filter templates by exact vCPU count. */ + templates_cpu_count: number + /** @description Filter templates by exact memory size in MB. */ + templates_memory_mb: number + /** @description Filter templates by visibility (true = public, false = internal). */ + templates_public: boolean + /** @description Case-insensitive substring match on template names, aliases, and template id. */ + templates_search: string + /** @description Sort column and direction. */ + templates_sort: + | 'name_asc' + | 'name_desc' + | 'cpu_count_asc' + | 'cpu_count_desc' + | 'memory_mb_asc' + | 'memory_mb_desc' + | 'created_at_asc' + | 'created_at_desc' + | 'updated_at_asc' + | 'updated_at_desc' } requestBodies: never headers: never diff --git a/src/core/shared/contracts/infra-api.types.ts b/src/core/shared/contracts/infra-api.types.ts index d4a49623a..e2a76ca09 100644 --- a/src/core/shared/contracts/infra-api.types.ts +++ b/src/core/shared/contracts/infra-api.types.ts @@ -706,13 +706,7 @@ export interface paths { } requestBody?: { content: { - 'application/json': { - /** - * Format: int32 - * @description Timeout in seconds from the current time after which the sandbox should expire - */ - timeout: number - } + 'application/json': components['schemas']['SandboxTimeoutRequest'] } } responses: { @@ -754,18 +748,7 @@ export interface paths { } requestBody: { content: { - 'application/json': { - /** @description List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. */ - allowOut?: string[] - /** @description List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. */ - denyOut?: string[] - /** @description Per-domain transform rules. Replaces all existing rules when provided. */ - rules?: { - [key: string]: components['schemas']['SandboxNetworkRule'][] - } - /** @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 - } + 'application/json': components['schemas']['SandboxNetworkUpdateConfig'] } } responses: { @@ -810,10 +793,7 @@ export interface paths { } requestBody?: { content: { - 'application/json': { - /** @description Duration for which the sandbox should be kept alive in seconds */ - duration?: number - } + 'application/json': components['schemas']['SandboxRefreshRequest'] } } responses: { @@ -855,10 +835,7 @@ export interface paths { } requestBody: { content: { - 'application/json': { - /** @description Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. */ - name?: string - } + 'application/json': components['schemas']['SandboxSnapshotRequest'] } } responses: { @@ -961,6 +938,7 @@ export interface paths { } 400: components['responses']['400'] 401: components['responses']['401'] + 403: components['responses']['403'] 500: components['responses']['500'] } } @@ -1650,7 +1628,10 @@ export interface paths { /** @description List all nodes */ get: { parameters: { - query?: never + query?: { + /** @description Identifier of the cluster */ + clusterID?: string + } header?: never path?: never cookie?: never @@ -1839,6 +1820,102 @@ export interface paths { patch?: never trace?: never } + '/admin/teams/{teamID}/api-keys': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + /** + * Create team API key as admin + * @description Creates a team API key for internal service workflows. + */ + post: { + parameters: { + query?: never + header?: never + path: { + /** @description Team ID */ + teamID: string + } + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['NewTeamAPIKey'] + } + } + responses: { + /** @description Team API key created successfully */ + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['CreatedTeamAPIKey'] + } + } + 400: components['responses']['400'] + 401: components['responses']['401'] + 403: components['responses']['403'] + 404: components['responses']['404'] + 500: components['responses']['500'] + } + } + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/admin/teams/{teamID}/api-keys/{apiKeyID}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + get?: never + put?: never + post?: never + /** + * Delete team API key as admin + * @description Deletes a team API key for internal service workflows. + */ + delete: { + 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 @@ -2199,8 +2276,12 @@ export interface components { * @description Identifier of the user */ id: string - /** @description Email of the user */ - email: 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 */ @@ -2265,6 +2346,19 @@ export interface components { [key: string]: components['schemas']['SandboxNetworkRule'][] } } + /** @description Network configuration update for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting a field clears it. */ + SandboxNetworkUpdateConfig: { + /** @description List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. */ + allowOut?: string[] + /** @description List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. */ + denyOut?: string[] + /** @description Per-domain transform rules. Replaces all existing rules when provided. */ + rules?: { + [key: string]: components['schemas']['SandboxNetworkRule'][] + } + /** @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 + } /** @description Transform rule applied to egress requests matching a domain pattern. */ SandboxNetworkRule: { transform?: components['schemas']['SandboxNetworkTransform'] @@ -2525,6 +2619,21 @@ export interface components { */ timeout: number } + SandboxTimeoutRequest: { + /** + * Format: int32 + * @description Timeout in seconds from the current time after which the sandbox should expire + */ + timeout: number + } + SandboxRefreshRequest: { + /** @description Duration for which the sandbox should be kept alive in seconds */ + duration?: number + } + SandboxSnapshotRequest: { + /** @description Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. */ + name?: string + } /** @description Team metric with timestamp */ TeamMetric: { /**