envless-sdk installs one import root, envless. Everything below except the types is a name you import from envless, and the types live in envless.types.
Clients
| Export | Signature |
|---|---|
Envless | (token=None, *, base_url=None, http_client=None, max_retries=None, timeout=None, user_agent=None, disable_update_notice=False) |
AsyncEnvless | The same arguments, with an httpx.AsyncClient for http_client |
create_client | The same class as Envless, under the TypeScript name |
envless | A live handle on the default Envless, built from the environment on first use |
init | (token=None, *, base_url=None, http_client=None, max_retries=None, timeout=None, user_agent=None, disable_update_notice=False) -> Envless, installs the default client and returns it |
get_client | () -> Envless |
reset_client | () -> None |
Every client reads ENVLESS_TOKEN and ENVLESS_API_ENDPOINT for the arguments you leave out. See Create a client and Async.
from envless import envless, init
init(timeout=10)
envless.products.list()Crypto
| Export | Signature |
|---|---|
encrypt_value | (plaintext: str, passphrase: str, workspace_unique_id: str) -> str |
decrypt_value | (ciphertext: str, passphrase: str, workspace_unique_id: str) -> str |
encrypt_with_key | (key: WorkspaceKey | bytes, plaintext: str) -> str |
decrypt_with_key | (key: WorkspaceKey | bytes, ciphertext: str) -> str |
derive_workspace_key | (passphrase: str, workspace_unique_id: str) -> WorkspaceKey |
import_workspace_key | (value: str) -> WorkspaceKey |
is_ciphertext | (value: object) -> TypeGuard[str] |
passphrase_strength | (passphrase: str) -> PassphraseValidation |
WorkspaceKey | A derived or imported AES-256 key. WorkspaceKey(raw) wraps exactly 32 bytes and raises ValueError on any other length. It compares in constant time, is hashable, prints as WorkspaceKey(<redacted>), and bytes(key) returns the raw key |
ENCRYPTION_PARAMETERS | The exact cryptographic constants, as a dict |
They are synchronous functions: encryption is CPU work. See Encryption for the recipe and for why you should derive the key once when writing a batch.
Helpers
| Export | Signature |
|---|---|
rotate_workspace_passphrase | (*, client, workspace_unique_id, from_, to, project, environment, product=None) -> RotatePassphraseResult, a coroutine with AsyncEnvless |
verify_webhook_signature | (*, payload: str | bytes, headers: Mapping[str, HeaderValue], secret: str, tolerance_seconds: float | None = None) -> WebhookEvent |
variable_name_validation | (name: object) -> NameValidation |
rotate_workspace_passphrase re-encrypts live variables and version history for one environment, which is the part that is easy to forget. verify_webhook_signature is the receiver side of Webhooks. variable_name_validation returns {'valid': ..., 'error': ...} without raising, for checking a name in your own form or importer against the rule every write applies before it sends anything.
Exceptions
| Export | Notes |
|---|---|
EnvlessError | The base of every exception below. |
EnvlessApiError | The API answered with a failure. Carries status, code, message, resource, field, request_id, retry_after_seconds. |
EnvlessNetworkError | Nothing came back: connection failure or timeout. Carries message, is_timeout, cause. |
DecryptionError | A value does not decrypt. Also a ValueError. |
WebhookVerificationError | A delivery fails verification. Also a ValueError. |
See Errors.
Constants
Enum-like values, so you never hard-code a string the API might reject. Each is a read-only namespace: read members as attributes, such as SCOPE_LEVELS.READ. Iterating one yields its values, in tests whether a string is one of them, and len() counts them, so if level not in SCOPE_LEVELS validates input.
| Export | Members |
|---|---|
SCOPE_RESOURCES | WORKSPACE, PRODUCTS, PRODUCT_ACCESS, PROJECTS, ENVIRONMENTS, VARIABLES, VERSIONS, MEMBERS, ROLES, KEYS, WEBHOOKS |
SCOPE_LEVELS | NONE, READ, WRITE |
VARIABLE_TYPE | STRING, NUMBER, BOOLEAN |
VARIABLE_VISIBILITY_LEVELS | SERVER, CLIENT |
WEBHOOK_SIGNATURE_HEADERS | ID, TIMESTAMP and SIGNATURE, the three signed headers, then EVENT (Envless-Event, a copy of the event type) and DELIVERY (Envless-Delivery, the id of one delivery, shared by its automatic retries, while a replay gets a new one), which sit outside the signed string |
PAGE_LIMITS | MAX_LIMIT is 100, DEFAULT_LIMIT is 25 |
VERSION | The package version, also __version__ |
import os
from envless import SCOPE_LEVELS, SCOPE_RESOURCES, VARIABLE_VISIBILITY_LEVELS, encrypt_value, envless
envless.keys.create(
{
'name': 'ci',
'scopes': {
SCOPE_RESOURCES.VARIABLES: SCOPE_LEVELS.READ,
SCOPE_RESOURCES.VERSIONS: SCOPE_LEVELS.READ,
},
}
)
workspace_id = envless.me.get()['workspaceId']
envless.variables.create(
'web',
'production',
{
'name': 'NEXT_PUBLIC_API_URL',
'value': encrypt_value('https://api.acme.com', os.environ['ENVLESS_PASSPHRASE'], workspace_id),
'visibility': VARIABLE_VISIBILITY_LEVELS.CLIENT,
},
)Types
Every request body and every response is a TypedDict in envless.types, named as in TypeScript. They describe plain dicts, so they cost nothing at runtime: annotate with them, and mypy or pyright checks every key you read or write.
from envless import envless
from envless.types import Page, VariableInput, VariableResource
body: VariableInput = {'name': 'PORT', 'value': 'v1:...', 'type': 'number'}
page: Page[VariableResource] = envless.variables.list('api', 'production', limit=10)Pages and pagination
Page, PaginationEnvelope, ProductAccessPage
Resources
IdentityResource, UserSummary, RoleSummary, ActorReference, ActorKeyReference, WorkspaceResource, CurrentWorkspaceResource, UpdatedWorkspaceResource, DeletedWorkspaceResource, CustomDomainResource, CustomDomainRecord, RemovedDomainResource, ProductResource, DeletedProductResource, ProductAccessEntry, GrantedProductAccessResource, RevokedProductAccessResource, ProjectResource, DeletedProjectResource, EnvironmentResource, EnvironmentSummaryResource, DeletedEnvironmentResource, VariableResource, DeletedVariableResource, DeletedVariablesResource, CreatedVariablesResource, VariableHistoryEvent, VersionResource, VersionDetailResource, VersionEntryResource, RolledBackVersionResource, ReencryptedEntriesResource, MemberResource, DeletedMemberResource, LeftWorkspaceResource, InviteResource, RevokedInviteResource, RoleResource, DeletedRoleResource, PermissionResource, PermissionsResource, KeyResource, CreatedKeyResource, DeletedKeyResource, WebhookResource, CreatedWebhookResource, DeletedWebhookResource, RotatedSecretResource, WebhookDeliveryResource, WebhookDeliveryDetailResource, RedeliveredWebhookResource, EventTypesResource
Inputs
WorkspaceCreate, WorkspacePatch, ProductCreate, ProductPatch, ProjectCreate, ProjectPatch, EnvironmentCreate, EnvironmentPatch, VariableInput, VariablePatch, VersionCreate, MemberInvite, MemberPatch, RoleCreate, RolePatch, KeyCreate, KeyPatch, WebhookCreate, WebhookPatch, ApplyChanges, ApplyBaselineEntry, ApplyUpdateEntry, ApplyResult, ReencryptEntryInput
Crypto and webhooks
EncryptionParameters, PassphraseValidation, NameValidation, RotatePassphraseResult, RotatePassphraseIssue, WebhookEvent, WebhookEventActor
Nested items
ApplyChangesDeletesItem, CreatedVariablesResourceCreatedItem, CreatedVariablesResourceSkippedItem, DeletedVariablesResourceDeletedItem, IdentityResourceKey, PermissionsResourceSystemRoles, ProjectResourceProduct, VersionDetailResourceVariablesItem
These name the dicts nested inside other results, for when you need to annotate one on its own. envless.types.client adds HeaderValue, which is str | Sequence[str] | None, and QueryValue, the values raw.request accepts in query. Options are keyword arguments rather than types, so there is no ListOptions or RequestScope in Python.
envless.constants holds the generated tables the SDK runs on. Only the names re-exported from envless itself are its public, stable surface.
Read shapes are not always the write shapes. workspace.get returns CurrentWorkspaceResource, with createdAt but no virtualEnvsEnabled, while workspace.update returns UpdatedWorkspaceResource, with virtualEnvsEnabled but no createdAt. The method pages call these out individually.
A worked example
import os
from envless import SCOPE_LEVELS, SCOPE_RESOURCES, encrypt_value, envless
passphrase = os.environ['ENVLESS_PASSPHRASE']
workspace_id = envless.me.get()['workspaceId']
product = envless.products.create({'name': 'Internal tools', 'slug': 'internal-tools'})
project = envless.projects.create({'name': 'API', 'slug': 'api', 'product': product['slug']})
for name in ('development', 'staging', 'production'):
envless.environments.create(project['slug'], {'name': name, 'slug': name}, product=product['slug'])
envless.variables.create_many(
'api',
'production',
[
{'name': 'DATABASE_URL', 'value': encrypt_value(os.environ['DATABASE_URL'], passphrase, workspace_id)},
{'name': 'PORT', 'value': encrypt_value('3000', passphrase, workspace_id), 'type': 'number'},
],
product=product['slug'],
)
envless.versions.create('api', 'production', {'note': 'Initial provisioning'}, product=product['slug'])
key = envless.keys.create(
{
'name': 'api-runner',
'scopes': {SCOPE_RESOURCES.VARIABLES: SCOPE_LEVELS.READ},
'projectIds': [project['id']],
}
)
print(key['secret'])