Workspaces and environments
Create and manage workspaces and environments, and scope API requests to them.
On this page
A workspace groups resources inside an organization, for example one per team or product. An environment is a stage inside a workspace, such as Production or Development. For how they fit into the hierarchy, see Organizations, workspaces and environments.
Every organization starts with a Default workspace that has two environments: Production (the default environment) and Development.
The examples use $ACCESS_TOKEN, an access token for your organization. Workspace and environment requests always act in the token's organization. A workspace or environment of another organization answers 404, exactly like one that doesn't exist.
Required permissions
| Operation | Permission |
|---|---|
| Create a workspace | workspace:workspace:create, granted at organization level |
| List, get, or get the default workspace | workspace:workspace:read |
| Update a workspace | workspace:workspace:update |
| Delete a workspace | workspace:workspace:delete |
| List, add or remove workspace members | workspace:member:manage |
| Create an environment | env:env:create |
| List, get, or get the default environment | env:env:read |
| Update an environment | env:env:update |
| Delete an environment | env:env:delete |
The owner, admin and member roles hold all of these by default. Viewers can only read. See Members.
Workspaces
Create a workspace
curl -X POST https://api.hexelstudio.com/v1/workspaces \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Payments", "slug": "payments", "description": "Payments team"}'const res = await fetch("https://api.hexelstudio.com/v1/workspaces", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Payments",
slug: "payments",
description: "Payments team",
}),
});
const workspace = await res.json();import os
import requests
res = requests.post(
"https://api.hexelstudio.com/v1/workspaces",
headers={"Authorization": f"Bearer {os.environ['ACCESS_TOKEN']}"},
json={"name": "Payments", "slug": "payments", "description": "Payments team"},
)
workspace = res.json()Display name.
Short identifier, for example payments.
Optional description.
The response is 201 Created:
{
"id": "<workspace-id>",
"organization_id": "<org-id>",
"name": "Payments",
"slug": "payments",
"description": "Payments team",
"is_default": false,
"is_active": true,
"created_by_user_id": "<user-id>",
"created_at": "2026-10-20T09:30:00Z",
"updated_at": "2026-10-20T09:30:00Z"
}A new workspace has no environments. Create them next.
List and get workspaces
# All workspaces of your organization
curl https://api.hexelstudio.com/v1/workspaces \
-H "Authorization: Bearer $ACCESS_TOKEN"
# One workspace
curl https://api.hexelstudio.com/v1/workspaces/$WORKSPACE_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"
# The default workspace
curl https://api.hexelstudio.com/v1/workspaces/default \
-H "Authorization: Bearer $ACCESS_TOKEN"The default workspace is the one with is_default: true. If there is none, /workspaces/default returns the oldest workspace.
Update a workspace
Send only the fields to change:
curl -X PATCH https://api.hexelstudio.com/v1/workspaces/$WORKSPACE_ID \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Payments platform"}'id, organization_id, created_by_user_id and is_default can't be changed; they are ignored if you send them. The response is the workspace.
Delete a workspace
curl -X DELETE https://api.hexelstudio.com/v1/workspaces/$WORKSPACE_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is 204 No Content. Deleting a workspace permanently deletes its environments and its member list. It can't be undone. This applies to the Default workspace too: it is not protected from deletion.
Workspace members
A workspace has its own member list. Only members of the workspace's organization can be added; anyone else is refused with 422:
curl -X POST https://api.hexelstudio.com/v1/workspaces/$WORKSPACE_ID/members \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id": "<user-id>", "role": "member"}'{
"error": "not_org_member",
"message": "the user is not a member of the workspace's organization"
}The user ID of a member of the organization.
A label for the person's role in the workspace.
List the members with GET /v1/workspaces/{workspace_id}/members. Remove one with DELETE /v1/workspaces/{workspace_id}/members/{member_id}, where member_id is the id of the workspace membership, not the user ID.
Environments
Create an environment
curl -X POST https://api.hexelstudio.com/v1/environments \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"workspace_id": "'"$WORKSPACE_ID"'", "name": "Staging", "slug": "staging", "type": "STAGING"}'The workspace to create the environment in. A missing or unknown workspace returns 404.
Display name.
Short identifier, for example staging.
The kind of stage, for example DEVELOPMENT, STAGING, PRODUCTION or SANDBOX.
Optional description.
The response is 201 Created:
{
"id": "<environment-id>",
"workspace_id": "<workspace-id>",
"name": "Staging",
"slug": "staging",
"type": "STAGING",
"is_default": false,
"is_protected": false,
"is_active": true,
"settings": "{}",
"created_by_user_id": "<user-id>",
"created_at": "2026-10-20T09:30:00Z",
"updated_at": "2026-10-20T09:30:00Z"
}is_protected is a label. The Production environment of the Default workspace has it set. Hexel Studio does not enforce it.
List and get environments
# All environments of your organization
curl https://api.hexelstudio.com/v1/environments \
-H "Authorization: Bearer $ACCESS_TOKEN"
# The environments of one workspace
curl "https://api.hexelstudio.com/v1/environments?workspace_id=$WORKSPACE_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
# One environment
curl https://api.hexelstudio.com/v1/environments/$ENVIRONMENT_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"
# The default environment of a workspace
curl "https://api.hexelstudio.com/v1/environments/default?workspace_id=$WORKSPACE_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"/environments/default needs workspace_id. It returns the workspace's environment with is_default: true, or the oldest one if none is marked.
Update an environment
curl -X PATCH https://api.hexelstudio.com/v1/environments/$ENVIRONMENT_ID \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Pre-production"}'id and workspace_id can't be changed; they are ignored if you send them. An environment can't move to another workspace.
Delete an environment
curl -X DELETE https://api.hexelstudio.com/v1/environments/$ENVIRONMENT_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is 204 No Content. The environment is deleted permanently. Default and protected environments can be deleted too.
Deleted entries in lists
Workspace and environment objects carry a deleted_at field. It is absent on live entries. If a list contains an entry with deleted_at set, treat it as deleted.
Scope a request to a workspace or environment
Send X-Workspace-Id and, inside it, X-Environment-Id to make a request in that workspace or environment:
curl https://api.hexelstudio.com/v1/... \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "X-Environment-Id: $ENVIRONMENT_ID"The headers choose where the permission check happens. They never change the organization, which always comes from the token.
- Permissions granted at organization level apply in every workspace and environment.
- Permissions granted for a workspace apply only when
X-Workspace-Idnames that workspace. Permissions granted for an environment need both headers. - A request is denied with
403whenX-Workspace-Idnames a workspace that isn't an active workspace of your organization, whenX-Environment-Idnames an environment outside that workspace, or whenX-Environment-Idis sent withoutX-Workspace-Id.
Creating a workspace is an organization-level operation: it ignores both headers. See Headers for every header the API reads.
Limits
Two plan limits apply. See Limits for your plan's values.
| Limit | What counts |
|---|---|
workspaces_per_org | Workspaces in the organization, including the Default workspace. |
environments_per_workspace | Environments in the workspace. |
Deleted workspaces and environments don't count. A create over the limit fails with 403:
{
"error": "quota_exceeded",
"quota_key": "environments_per_workspace",
"limit": 2,
"current": 2,
"message": "plan limit reached: this workspace can have at most 2 environments; upgrade the plan or contact Hexel"
}