/projects/{slug}/environments/{environmentSlug}/variables/{name}/historyAudit trail for one variable, newest first. Events are recorded per variable id, so history follows the row through renames but does not follow a name that was deleted and re-created, that name starts a fresh id with a fresh history.
No values, ciphertext or otherwise, are ever included. metadata carries small change hints such as nameChanged, valueChanged, previousName, rolledBackFromVersion or source, and its keys vary by event type.
actor is null for changes made by an API key whose owner record is gone, or by internal processes.
Plan gate. Free workspaces see only the latest event and the response carries historyLimited: true with hiddenCount telling you how many older events exist. Dev, Team and Enterprise see the full history. Nothing is deleted on a downgrade, the older events come back on upgrade.
Requires the variables:read scope.
curl --request GET \
--url 'https://api.envless.cloud/projects/<slug>/environments/<environmentSlug>/variables/<name>/history' \
--header 'Authorization: Bearer ev_sk_xxxxxxxxxxxx'https://api.envless.cloud/projects/{slug}/environments/{environmentSlug}/variables/{name}/historyResponse 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
slugstringrequiredProject slug or project uniqueId.
environmentSlugstringrequiredEnvironment slug.
namestringrequiredCurrent variable name, matched without regard to case. The variable must still be live, history for a deleted variable is not reachable by name.
Query parameters
productstringProduct slug, to disambiguate a project slug shared across products.
limitintegermin 1max 100Page size, 1 to 100.
Default:
25offsetintegermin 0Rows to skip.
Default:
0
Responses
200OK
List a variable's history succeeded.
successbooleanrequiredcodeintegerrequiredThe HTTP status of this response, repeated in the body.
messagestringrequirederrornullrequireddataobjectrequiredShow child attributesHide child attributes
eventsobject[]requiredShow child attributesHide child attributes
idstringrequiredtypeenum<string>requiredOptions:
"created","updated","deleted","restored"metadataobject | nullFree-form change hints. Never contains values.
actorobject | nullShow child attributesHide child attributes
idstringrequirednamestring | nullemailstring | null
createdAtstringrequireddate-time
paginationobjectrequiredShow child attributesHide child attributes
limitintegerrequiredoffsetintegerrequiredtotalCountintegerrequiredhasMorebooleanrequired
historyLimitedbooleanrequiredTrue when the plan hides part of the history. Free workspaces see only the latest event; Dev, Team and Enterprise see everything.
historyLimitintegerrequiredHow many entries the plan makes visible. 1 on Free, a very large sentinel on unlimited plans.
hiddenCountintegerrequiredHow many older events exist but are hidden by the plan. 0 when nothing is hidden.
_reqobjectShow child attributesHide child attributes
reqIdstringresTimeinteger | null
{
"success": true,
"code": 200,
"message": "OK",
"error": null,
"data": {
"events": [
{
"id": "<string>",
"type": "created",
"metadata": {},
"actor": {
"id": "<string>",
"name": "<string>",
"email": "<string>"
},
"createdAt": "2026-05-30T10:14:22.000Z"
}
],
"pagination": {
"limit": 1,
"offset": 1,
"totalCount": 1,
"hasMore": true
},
"historyLimited": true,
"historyLimit": 1,
"hiddenCount": 1
},
"_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 is outside 1 to 100 or either pagination value is not a whole number.
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.
402Payment Required
plan.upgrade_required - the project is locked by the plan's project limit. Free workspaces are not refused here, they simply receive only the latest event with historyLimited: true.
403Forbidden
auth.key_project_restricted- the key's project allowlist excludes this project, orauth.product_restrictedfor an inaccessible private product.auth.workspace_two_factor_required- the workspace requires two-factor authentication and the key's owner has not turned it on.auth.scope_missing- the key lacks a scope this operation requires;error.resourcenames it.
404Not Found
project.not_found- no project with that slug in the workspace.request.not_found- the environment does not exist, or it holds no live variable of that name.
409Conflict
project.ambiguous_product - the project slug exists under more than one 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.
On the Free plan only the latest entry of the trail is returned, with historyLimited: true, historyLimit: 1 and hiddenCount for the entries that exist but are not listed. The full trail needs Dev or above. See Plans and billing.