envless-sdk is the official Ruby client for the Envless API. It has every method of the TypeScript SDK, all 113 across 13 namespaces, under the same names in snake_case, ships RBS signatures for every request and response, and has no runtime dependencies.
bundle add envless-sdkgem install envless-sdkSet ENVLESS_TOKEN to an API key and the shared client works with no setup code at all.
require "envless/sdk"
workspace = Envless.workspace.get
Envless.projects.iterate do |project|
puts "#{workspace[:slug]} #{project[:slug]}"
endPrefer an explicit instance, or need more than one workspace? Envless::Client.new gives you one to pass around. See Create a client.
What this is, and what it is not
The Ruby SDK administers a workspace from code. It is the Ruby twin of @goenvless/sdk: the same methods, the same requests on the wire, the same retries and the same error codes. It is not how a Ruby application loads its own variables at boot.
envless-sdk | @goenvless/sdk | @goenvless/cli | |
|---|---|---|---|
| Purpose | Administers a workspace from Ruby | Administers a workspace from TypeScript | Runs the envless command, and loads variables into any process |
| Audience | Provisioning scripts, Rake tasks, Rails back offices, CI, platform teams | The same, on Node, Bun, Deno or Workers | People at a shell, CI steps, and apps in any language |
| Auth | An API key, and nothing else | An API key, and nothing else | A device session, or a machine key for reads |
| Shape | Envless.projects.create(name: "API", slug: "api") | envless.projects.create({ ... }) | envless run -- bin/rails server |
If you want your Rails app to read DATABASE_URL at boot, use the Ruby runtime, envless-env, or wrap it with envless run -- bin/rails server, which decrypts the environment and hands it to the process as ordinary environment variables, or write a .env file with envless sync. ENV then has everything. If you want a script that creates a project, adds three environments and seeds their variables, you are in the right place.
Design
Typed with RBS
The gem ships signatures in sig/ with a record type for every request body and every response, so Steep checks the keys and the types of each call, and editors that read RBS complete them.
No dependencies
HTTP is Net::HTTP and the encryption is OpenSSL, both from the standard library. Ruby 3.2 or newer, and nothing to compile.
Threads and forks
One client is safe to share between threads and keeps a pool of kept-alive connections. After a fork, in Puma or Sidekiq, each process opens its own.
Honest errors
Every API failure is an Envless::ApiError carrying the API's stable machine-readable code, and every transport failure is an Envless::NetworkError. Both inherit Envless::Error.
The six 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. See Authentication.
Values are ciphertext, always
The server never sees plaintext and cannot encrypt for you. Encrypt before writing, decrypt after reading. See Encryption.
Bodies are keywords, results are Hashes with the API field names
A request body goes in as keyword arguments, or as one Hash, keyed exactly as the API reference spells them: defaultValue:, projectIds:, virtualEnvsEnabled:. Every result is a Hash with Symbol keys, read as variable[:updatedAt]. Method names and options are snake_case, field names are not.
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: "billing".
Lists come in three flavours
list for one page, list_all 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
| Namespace | What it covers |
|---|---|
me | The identity behind the key |
workspace | The bound workspace, and its custom domain |
workspaces | Every workspace the key owner belongs to |
products | Products, their projects, and per-product access grants |
projects | Projects inside a product |
environments | Environments, including public exposure |
variables | The encrypted values, and atomic sync |
versions | Snapshots, rollback, and history re-encryption |
members | Membership, roles and departures |
invites | Outstanding invitations |
roles | System and custom roles, and permissions |
keys | The credentials that reach this API |
webhooks | Endpoints, secrets, deliveries and replays |
Anything not covered by a method is still reachable through client.raw.request(path, method:, query:, body:), which unwraps the response envelope and raises the same typed errors.
Every method has a twin
Each Ruby method page links to the same method in the TypeScript SDK, and each one sends exactly the request its TypeScript twin sends. A parity check in the repository replays all 113 methods against the requests the TypeScript SDK records, with no options, with every option set and with the body passed as keywords, and fails the build on any difference in the request, the retries, the errors or the result. Ciphertext from this gem, the TypeScript and Python SDKs, the CLI and the dashboard is interchangeable.