Workspace

Create a workspace

Creates a new workspace owned by the API key's owner.

POST/workspaces

Creates a new workspace owned by the API key's owner. The caller becomes its owner with the full owner role.

The new workspace is not reachable with the key that created it. Keys are bound to a single workspace at creation, so the response is the last thing this key will ever see of it. Mint a key inside the new workspace, in the dashboard or from a key that already lives there, before you can add products, projects or variables.

slug is optional and defaults to a slugified name. Slugs are globally unique across every Envless workspace, not just yours, so a common name may already be taken. Each user may own at most 100 workspaces.

Unlike creating a workspace in the dashboard, this does not change which workspace the owner currently has selected there. Emits the workspace.created webhook event, delivered to the endpoints of the new workspace.

Requires the workspace:write scope.

curl --request POST \
  --url 'https://api.envless.cloud/workspaces' \
  --header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Billing",
  "slug": "billing"
}'
Try itPOSThttps://api.envless.cloud/workspaces

Authorization

  • Authorizationstringrequiredheader

    Bearer token: an Envless API key (ev_sk_…), created in the dashboard or with POST /keys. The key is bound to one workspace; every request runs inside it.

Request body

application/json · required

  • namestringrequiredmin length 3max length 50

    Display name, 3 to 50 characters.

  • slugstringmin length 3max length 50pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$

    URL slug, 3 to 50 characters, lowercase letters, digits and single hyphens only. Globally unique across all Envless workspaces. Defaults to a slugified name.

Responses

201Created

Create a workspace succeeded.

  • successbooleanrequired
  • codeintegerrequired

    The HTTP status of this response, repeated in the body.

  • messagestringrequired
  • errornullrequired
  • dataobjectrequired
    Show child attributesHide child attributes
    • idstringrequired
    • namestringrequired
    • slugstringrequired
    • iconstring | null
    • planstringrequired

      The workspace billing plan.

    • virtualEnvsEnabledbooleanrequired

      Whether exposed (virtual) environments are allowed in this workspace.

    • customDomainstring | null

      The active custom domain hostname, or null when none is verified.

    • currentbooleanrequired

      True for the workspace the API key is bound to. Every other endpoint operates inside that one.

    • createdAtstringrequireddate-time
  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": true,
    "code": 201,
    "message": "OK",
    "error": null,
    "data": {
        "id": "<string>",
        "name": "<string>",
        "slug": "<string>",
        "icon": "<string>",
        "plan": "<string>",
        "virtualEnvsEnabled": true,
        "customDomain": "<string>",
        "current": true,
        "createdAt": "2026-05-30T10:14:22.000Z"
    },
    "_req": {
        "reqId": "<string>",
        "resTime": 1
    }
}

Error envelope

Every failure below returns the same shape with success: false; branch on error.code, never on message.

  • successbooleanrequired
  • codeintegerrequired

    The HTTP status of this response, repeated in the body.

  • messagestringrequired
  • errorobjectrequired

    Machine-readable failure detail. Null on success.

    Show child attributesHide child attributes
    • codeenum<string>required

      Stable error code. Branch on this, never on message.

      Options: "auth.api_key_required", "auth.api_key_not_found", "auth.api_key_user_missing", "auth.api_key_owner_not_member", "auth.scope_missing", "auth.scope_ceiling_exceeded", "auth.key_project_restricted", "auth.product_restricted", "auth.permission_denied", "auth.self_mutation_forbidden", "auth.step_up_required", "auth.step_up_fresh_required", "auth.cli_token_not_accepted", "auth.two_factor_required", "auth.workspace_two_factor_required", "auth.two_factor_invalid", "auth.two_factor_replayed", "auth.two_factor_locked", "auth.two_factor_not_enabled", "auth.two_factor_already_enabled", "request.body_must_be_json", "request.body_invalid", "request.body_too_large", "request.rate_limited", "request.invalid_pagination", "workspace.not_found", "workspace.slug_conflict", "workspace.limit_reached", "workspace.delete_last", "workspace.delete_too_early", "workspace.delete_has_projects", "domain.not_configured", "domain.not_found", "domain.invalid", "domain.taken", "domain.attempt_limit", "product.not_found", "product.slug_conflict", "product.locked", "project.not_found", "project.ambiguous_product", "project.slug_conflict", "environment.not_found", "environment.slug_conflict", "environment.expose_disabled", "environment.expose_private_product", "variable.not_found", "variable.name_conflict", "variable.ciphertext_invalid", "variable.baseline_drifted", "version.not_found", "member.not_found", "member.already_exists", "member.self_invite", "member.self_mutation", "member.owner_immutable", "member.owner_cannot_leave", "member.seat_limit", "invite.already_pending", "role.owner_not_assignable", "role.limit_reached", "role.immutable", "role.in_use", "invite.not_found", "invite.expired", "role.not_found", "role.name_conflict", "key.not_found", "key.limit_reached", "webhook.not_found", "webhook.delivery_not_found", "plan.limit_reached", "plan.upgrade_required", "request.not_found", "request.method_not_allowed", "request.conflict", "auth.unauthorized", "auth.forbidden", "request.invalid", "server.error"

    • resourcestring

      The resource or scope the failure relates to.

    • fieldstring

      The request field that caused the failure.

  • dataobject | nullrequired

    Null on most failures. A plan limit carries { limit, current }.

  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": false,
    "code": 400,
    "message": "Something went wrong.",
    "error": {
        "code": "auth.api_key_required",
        "resource": "<string>",
        "field": "<string>"
    },
    "data": {},
    "_req": {
        "reqId": "<string>",
        "resTime": 1
    }
}

400Bad Request

  • request.body_must_be_json - the body is not valid JSON.
  • request.body_invalid - name is missing or empty, or name/slug fails validation.

401Unauthorized

  • auth.api_key_required - the Authorization header is missing, or its value does not start with ev_sk_.
  • auth.api_key_not_found - the key is unknown, inactive or revoked.
  • auth.api_key_user_missing - the key is valid but its owner no longer exists.
  • auth.api_key_owner_not_member - the key's owner is no longer a member of the key's workspace.

403Forbidden

  • auth.scope_missing - the key lacks workspace:write.
  • auth.workspace_two_factor_required - the workspace requires two-factor authentication and the key's owner has not turned it on.

409Conflict

  • workspace.slug_conflict - another workspace anywhere on Envless already owns that slug.
  • workspace.limit_reached - the owner already has 100 workspaces.

413Payload Too Large

request.body_too_large - the request body is larger than 1 MiB (1,048,576 bytes).

500Internal Server Error

server.error - an unexpected error; retry with backoff.

503Service Unavailable

server.error - the API is in maintenance mode or is missing its configuration; retry with backoff.