Projects

List projects

Lists projects across every product the key owner can reach.

GET/projects

Lists projects across every product the key owner can reach. Project slugs are unique per product, not per workspace, so this flat list can legitimately contain two projects with the same slug in different products - always key off id, or off the product.slug + slug pair. Pass ?product=<productSlug> to narrow to one product. Project-restricted keys see only their allowed project ids; owners without products.manage_private see only public products plus products they hold a grant on. Every row carries its product object so you can disambiguate without a second call.

Requires the projects:read scope.

curl --request GET \
  --url 'https://api.envless.cloud/projects' \
  --header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx'
Try itGEThttps://api.envless.cloud/projects
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.

Query parameters

  • productstring

    Filter to a single product. Accepts a product slug or a product uniqueId. An unknown or inaccessible value returns 404 product.not_found with field product - it does not silently return an empty page.

  • limitintegermin 1max 100

    Rows per page.

    Default: 25

  • offsetintegermin 0

    Rows to skip.

    Default: 0

  • searchstring

    Case-insensitive substring match against project name and slug.

  • orderenum<string>

    name sorts A to Z, asc and desc sort oldest and newest first on createdAt. Any other value, including omitting it, sorts by name.

    Options: "name", "asc", "desc"

Responses

200OK

List projects succeeded.

  • successbooleanrequired
  • codeintegerrequired

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

  • messagestringrequired
  • errornullrequired
  • dataobjectrequired
    Show child attributesHide child attributes
    • projectsobject[]required
      Show child attributesHide child attributes
      • idstringrequired
      • namestringrequired
      • slugstringrequired
      • iconstring | null
      • productobject | nullrequired
        Show child attributesHide child attributes
        • idstringrequired
        • slugstring | null
        • namestring | null
      • createdAtstringrequireddate-time
      • updatedAtstring | nulldate-time
    • paginationobjectrequired
      Show child attributesHide child attributes
      • limitintegerrequired
      • offsetintegerrequired
      • totalCountintegerrequired
      • hasMorebooleanrequired
  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": true,
    "code": 200,
    "message": "OK",
    "error": null,
    "data": {
        "projects": [
            {
                "id": "<string>",
                "name": "<string>",
                "slug": "<string>",
                "icon": "<string>",
                "product": {
                    "id": "<string>",
                    "slug": "<string>",
                    "name": "<string>"
                },
                "createdAt": "2026-05-30T10:14:22.000Z",
                "updatedAt": "2026-05-30T10:14:22.000Z"
            }
        ],
        "pagination": {
            "limit": 1,
            "offset": 1,
            "totalCount": 1,
            "hasMore": true
        }
    },
    "_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.invalid_pagination - limit or offset is not a whole number, or limit falls outside 1-100.

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 projects:read.
  • auth.workspace_two_factor_required - the workspace requires two-factor authentication and the key's owner has not turned it on.

404Not Found

product.not_found - ?product= names a product that does not exist or that the key owner cannot reach. The error carries field product.

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.