Getting started

SDK overview

A typed TypeScript client for the Envless API. Every workspace, product, project, environment, variable, version, member, role, key and webhook, driven from code.

@goenvless/sdk is the official TypeScript client for the Envless API. It covers 100% of the public API: every documented operation has a method, and a parity check in the SDK repository fails the build if that ever stops being true.

bash
npm install @goenvless/sdk

Initialise it once at startup, then use it anywhere without passing the key again.

app.ts
import { init } from '@goenvless/sdk'

init({ token: process.env.ENVLESS_TOKEN })
anywhere-else.ts
import { envless } from '@goenvless/sdk'

const workspace = await envless.workspace.get()

for await (const project of envless.projects.iterate()) {
    console.log(project.slug)
}

Prefer an explicit instance, or need more than one workspace? createClient() gives you one to pass around. See Create a client.

Working in Python? envless-sdk is this SDK for Python, on PyPI and imported as envless: the same 113 methods under snake_case names, the same requests on the wire, with Envless for synchronous code and AsyncEnvless for asyncio and trio. Every method page here links to its Python twin.

Working in Ruby? envless-sdk is this SDK for Ruby, on RubyGems: the same 113 methods under snake_case names, the same requests on the wire, typed with RBS and free of runtime dependencies. Every method page here links to its Ruby twin as well.

Working in PHP? envless/sdk is this SDK for PHP, on Packagist: the same 113 methods under the same names, the same requests on the wire, typed for PHPStan and Psalm and free of Composer dependencies. Every method page here links to its PHP twin too.

What this is, and what it is not

Envless ships three independent packages. They solve different problems and none depends on the others.

@goenvless/sdk@goenvless/env@goenvless/cli
PurposeAdministers a workspace from codeLoads variables into a running appRuns the envless command in a terminal
AudienceProvisioning scripts, internal tools, CI, platform teamsApplication runtimePeople at a shell, and CI steps
AuthAn API key, and nothing elseA machine key, plus a workspace key or passphraseA device session, or a machine key for reads
Shapeenvless.projects.create(...)import { env } from '@goenvless/env/server'envless run -- node server.js

If you want your app to read DATABASE_URL at boot, you want @goenvless/env. If you want a script that creates a project, adds three environments and seeds their variables, you are in the right place.

Design

Fully typed

Every request body, query option and response is a named exported type. No any at the boundary.

Runtime agnostic

fetch and Web Crypto only. No Node built-ins, no filesystem, no Buffer. Runs on Node, Bun, Deno and Cloudflare Workers. Server side only: it refuses to start in a browser.

Zero dependencies

Nothing in dependencies. The whole client is one tree-shakeable module.

Honest errors

Every API failure is an EnvlessApiError carrying the API's stable machine-readable code, and every transport failure is an EnvlessNetworkError.

The five things worth knowing up front

A key is bound to one workspace

The workspace comes from the key, never from the request. envless.workspaces.list() shows you the others the key owner belongs to, but the rest of the SDK only ever acts inside the bound one. Reaching a second workspace means a second client, which is why init holds one and createClient exists for the rest. See Authentication.

Values are ciphertext, always

The server never sees plaintext and cannot encrypt for you. Encrypt before writing, decrypt after reading. See Encryption.

Projects live inside products

Project slugs are unique per product, not per workspace. When a slug is ambiguous the call fails with 409 project.ambiguous_product and you disambiguate with { product }.

Lists come in three flavours

list for one page, listAll for everything, iterate for a stream. See Pagination.

Sync environments with apply, not a loop

variables.apply runs a whole change set in one transaction and detects concurrent edits. A loop of single calls does neither.

Namespaces

NamespaceWhat it covers
meThe identity behind the key
workspaceThe bound workspace, and its custom domain
workspacesEvery workspace the key owner belongs to
productsProducts, their projects, and per-product access grants
projectsProjects inside a product
environmentsEnvironments, including public exposure
variablesThe encrypted values, and atomic sync
versionsSnapshots, rollback, and history re-encryption
membersMembership, roles and departures
invitesOutstanding invitations
rolesSystem and custom roles, and permissions
keysThe credentials that reach this API
webhooksEndpoints, secrets, deliveries and replays

Anything not covered by a method is still reachable through envless.raw.request(path, options), which unwraps the response envelope and throws the same typed errors.

Going deeper

What the package guarantees, measured against the built bundle rather than the readme.