Environments

Create an environment

Creates an environment in a project.

POST/projects/{slug}/environments

Creates an environment in a project. Both name and slug are required; the slug is slugified server-side before validation and must be unique within the project. Project slugs are unique per product, so pass ?product=<productSlug> when the project slug is ambiguous - note the body is parsed and validated before the project is resolved, so a malformed body returns 400 even when the project reference is also wrong. The environment allowance is counted per project (Free 2, Dev 3, Team 15, Enterprise unlimited), not per workspace like products and projects, and is checked before the slug conflict. exposed cannot be set over the API and always comes back false on this response even though the stored value may differ later.

Requires the environments:write scope.

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

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.

Path parameters

  • slugstringrequired

    Project slug, or the project's uniqueId. A UUID-shaped value is resolved as an id first, then as a slug.

Query parameters

  • productstring

    Product slug used to disambiguate the project slug. Required whenever the same project slug exists in more than one product.

Request body

application/json · required

  • namestringrequiredmin length 2max length 50

    Display name. Required.

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

    Required. Unique within the project. Slugified server-side before validation.

Responses

201Created

Create an environment succeeded.

  • successbooleanrequired
  • codeintegerrequired

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

  • messagestringrequired
  • errornullrequired
  • dataobjectrequired
    Show child attributesHide child attributes
    • idstringrequired
    • namestringrequired
    • slugstringrequired
    • projectIdstringrequired
    • exposedbooleanrequired

      When true every published version is readable without an API key. Read-only here; change it in the dashboard.

    • createdAtstringrequireddate-time
    • updatedAtstring | nulldate-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>",
        "projectId": "<string>",
        "exposed": true,
        "createdAt": "2026-05-30T10:14:22.000Z",
        "updatedAt": "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 absent or not parseable as JSON.
  • request.body_invalid - name or slug is missing or not a string, or either fails length or slug-pattern 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.

402Payment Required

plan.upgrade_required - the project already holds its plan's environment allowance (Free 2, Dev 3, Team 15 per project; Enterprise is unlimited and never hits this). data carries { limit, current }.

403Forbidden

  • auth.scope_missing - the key lacks environments:write.
  • auth.key_project_restricted - the project is not in the key's project allowlist.
  • auth.product_restricted - the project's product is private and the key owner holds no grant on it.
  • auth.workspace_two_factor_required - the workspace requires two-factor authentication and the key's owner has not turned it on.

404Not Found

project.not_found - no project matches in scope for this key.

409Conflict

  • project.ambiguous_product - the project slug resolves to projects in more than one product; retry with ?product=<productSlug>.
  • plan.limit_reached - reserved for a plan whose environment allowance sits at or above the 1,000 fair-use cap. No current tier can trigger it: Free, Dev and Team fail with 402 plan.upgrade_required at their own allowance instead, and Enterprise is unlimited and never hits a plan limit. data carries { limit, current }.
  • environment.slug_conflict - the resolved slug is already taken inside the project.
  • project.not_found - the project was deleted while the environment was being created.

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.