envless/sdk is the official PHP client for the Envless API. It has every method of the TypeScript SDK, all 113 across 13 namespaces, under the same names, ships a PHPStan and Psalm array shape for every request and response, and has no Composer dependencies.
composer require envless/sdkSet ENVLESS_TOKEN to an API key and a client needs no arguments at all.
use Envless\Envless;
$envless = new Envless();
$workspace = $envless->workspace->get();
foreach ($envless->projects->iterate() as $project) {
echo $workspace['slug'], ' ', $project['slug'], PHP_EOL;
}Want one shared client for the whole app, or more than one workspace? See Create a client.
What this is, and what it is not
The PHP SDK administers a workspace from code. It is the PHP 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 PHP application loads its own variables at boot.
envless/sdk | @goenvless/sdk | @goenvless/cli | |
|---|---|---|---|
| Purpose | Administers a workspace from PHP | Administers a workspace from TypeScript | Runs the envless command, and loads variables into any process |
| Audience | Provisioning scripts, Artisan and Symfony commands, 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 -- php artisan serve |
If you want your Laravel or Symfony app to read DATABASE_URL at boot, use the PHP runtime, envless/env, or wrap the process with envless run, which decrypts the environment and hands it over as ordinary environment variables, or write a .env file with envless sync. If you want a script that creates a project, adds three environments and seeds their variables, you are in the right place.
Design
Typed for PHPStan and Psalm
Every request body and response is an array shape on Envless\Types, and every method declares the shapes it takes and returns, so PHPStan and Psalm check the keys and the types of each call, and editors complete them.
No dependencies
HTTP is cURL and the encryption is OpenSSL, both standard PHP extensions. PHP 8.2 or newer, and nothing else to install.
Your HTTP client, if you want one
Requests go through cURL by default. Guzzle or any other PSR-18 client plugs in through Envless\Http\Psr18HttpClient, and the retries, the deadline and the redirect rules stay in the SDK.
Honest errors
Every API failure throws Envless\Exception\ApiException carrying the API's stable errorCode, and every transport failure throws Envless\Exception\NetworkException. Both implement Envless\Exception\EnvlessException.
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 and results are arrays with the API field names
A request body is one array keyed exactly as the API reference spells it: defaultValue, projectIds, virtualEnvsEnabled. Every result is the decoded array, read as $variable['updatedAt']. The options of a call are named arguments: product:, limit:, timeout:.
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, listAll for everything, iterate for a stream. See Pagination.
Sync environments with apply, not a loop
$envless->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 $envless->raw->request($path, $method, query: [...], body: [...]), which unwraps the response envelope and throws the same typed exceptions.
Every method has a twin
Each PHP 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 and with every option set, and fails the build on any difference in the request, the retries, the errors or the result. Every example on the method pages is type-checked with PHPStan at its strictest level and run against a sample API before a release. Ciphertext from this package, the TypeScript, Python and Ruby SDKs, the CLI and the dashboard is interchangeable.