/projectsLists 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'https://api.envless.cloud/projectsResponse headers
Authorization
AuthorizationstringrequiredheaderBearer token: an Envless API key (
ev_sk_…), created in the dashboard or withPOST /keys. The key is bound to one workspace; every request runs inside it.
Query parameters
productstringFilter to a single product. Accepts a product slug or a product uniqueId. An unknown or inaccessible value returns 404
product.not_foundwith fieldproduct- it does not silently return an empty page.limitintegermin 1max 100Rows per page.
Default:
25offsetintegermin 0Rows to skip.
Default:
0searchstringCase-insensitive substring match against project name and slug.
orderenum<string>namesorts A to Z,ascanddescsort oldest and newest first on createdAt. Any other value, including omitting it, sorts by name.Options:
"name","asc","desc"
Responses
200OK
List projects succeeded.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrornullrequireddataobjectrequiredShow child attributesHide child attributes
projectsobject[]requiredShow child attributesHide child attributes
idstringrequirednamestringrequiredslugstringrequirediconstring | nullproductobject | nullrequiredShow child attributesHide child attributes
idstringrequiredslugstring | nullnamestring | null
createdAtstringrequireddate-timeupdatedAtstring | nulldate-time
paginationobjectrequiredShow child attributesHide child attributes
limitintegerrequiredoffsetintegerrequiredtotalCountintegerrequiredhasMorebooleanrequired
_reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | 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.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrorobjectrequiredMachine-readable failure detail. Null on success.
Show child attributesHide child attributes
codeenum<string>requiredStable 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"resourcestringThe resource or scope the failure relates to.
fieldstringThe request field that caused the failure.
dataobject | nullrequiredNull on most failures. A plan limit carries
{ limit, current }._reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | 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- theAuthorizationheader is missing, or its value does not start withev_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.