Product access

Grant product access

Grants one workspace member access to a product.

PUT/products/{slug}/access/{memberId}

Grants one workspace member access to a product. Idempotent: re-granting an existing grant succeeds and still returns granted: true. Takes no request body - the two path segments are the whole input. {memberId} is a workspace member id (the id on GET /members and on GET /products/{slug}/access), not a user id; passing a user id returns 404 member.not_found. Granting on a public product is allowed and simply records the grant, which only starts mattering if the product is later switched to private. Returns 200, not 201, because the grant is upserted rather than created.

Requires the product-access:write scope.

curl --request PUT \
  --url 'https://api.envless.cloud/products/<slug>/access/<memberId>' \
  --header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx'
Try itPUThttps://api.envless.cloud/products/{slug}/access/{memberId}
Path 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

    Product slug, or the product's uniqueId. A UUID-shaped value is resolved as an id only.

  • memberIdstringrequired

    Workspace member id, the same id space as PATCH /members/{id}. Not a user id.

Responses

200OK

Grant product access succeeded.

  • successbooleanrequired
  • codeintegerrequired

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

  • messagestringrequired
  • errornullrequired
  • dataobjectrequired
    Show child attributesHide child attributes
    • idstringrequired

      Workspace member id that was granted.

    • userobject | nullrequired
      Show child attributesHide child attributes
      • idstringrequired
      • namestringrequired
      • emailstringrequired
      • avatarstring | nullrequired
    • grantedbooleanrequired

      Always true on success, including when the grant already existed.

  • _reqobject
    Show child attributesHide child attributes
    • reqIdstring
    • resTimeinteger | null
{
    "success": true,
    "code": 200,
    "message": "OK",
    "error": null,
    "data": {
        "id": "<string>",
        "user": {
            "id": "<string>",
            "name": "<string>",
            "email": "<string>",
            "avatar": "<string>"
        },
        "granted": 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": 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 - 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

product.locked - the product sits beyond the plan's product allowance.

403Forbidden

  • auth.key_project_restricted - the key is scoped to a list of project ids; such keys cannot manage product access.
  • auth.scope_missing - the key lacks product_access:write.
  • 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 it is private and the key owner has no grant.
  • member.not_found - no live member of this workspace has that member id - most often because a user id was passed instead.

409Conflict

product.not_found - the product was deleted while the grant was being created.

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.