/products/{slug}Fetches a single product by slug or by id. Unlike the project routes there is no id-then-slug fallback: if the path segment matches the UUID shape it is looked up as an id only, otherwise as a slug only. Private products the key owner has no grant on return 404 rather than 403, so callers cannot probe for their existence. This is the only product read that includes locked, computed against the workspace's current plan.
Requires the products:read scope.
curl --request GET \
--url 'https://api.envless.cloud/products/<slug>' \
--header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx'https://api.envless.cloud/products/{slug}Response 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.
Path parameters
slugstringrequiredProduct slug, or the product's uniqueId. A UUID-shaped value is resolved as an id and never falls back to a slug lookup.
Responses
200OK
Get a product succeeded.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrornullrequireddataobjectrequiredShow child attributesHide child attributes
idstringrequirednamestringrequiredslugstringrequirediconstring | nullisPrivatebooleanrequiredlockedbooleanTrue when the product exceeds the plan limit and is read-only until you upgrade.
createdAtstringrequireddate-timeupdatedAtstring | nulldate-time
_reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | null
{
"success": true,
"code": 200,
"message": "OK",
"error": null,
"data": {
"id": "<string>",
"name": "<string>",
"slug": "<string>",
"icon": "<string>",
"isPrivate": true,
"locked": 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.
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": 401,
"message": "Something went wrong.",
"error": {
"code": "auth.api_key_required",
"resource": "<string>",
"field": "<string>"
},
"data": {},
"_req": {
"reqId": "<string>",
"resTime": 1
}
}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 products: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 - no product matches, or the product is private and the key owner has no grant on it.
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.