1import{j as e}from"./jsx-runtime.u17CrQMm.js";const d={title:"API Changelog",description:"Version history for the Envless HTTP API."},l="\n<p>Version history for the Envless HTTP API. The TypeScript client over it ships separately as `@goenvless/sdk`, the Python and Ruby clients as `envless-sdk`, the PHP client as `envless/sdk`, and each has its own changelog, [TypeScript](/docs/sdk/changelog), [Python](/docs/python/changelog), [Ruby](/docs/ruby/changelog) and [PHP](/docs/php/changelog). The `envless` CLI ships as `@goenvless/cli` and the application runtime as `@goenvless/env`.</p>\n\n<Update label='October 5, 2026' description='API fixes'>\n **Project lookups now answer with the codes this reference documents.** Seventeen routes that take a project slug answered an ambiguous slug with the generic `409 request.conflict` and a missing project with `404 request.not_found`. They now send `409 project.ambiguous_product` and `404 project.not_found`, like the routes that already did. The statuses are unchanged, so code that branches on them keeps working.\n\n - Affected: reading, updating, deleting and exposing environments, reading, updating and deleting projects, every variable route under an environment except single create and apply, and listing and publishing versions\n - `POST .../expose` answered an ambiguous slug with `409 project.not_found`, and now sends `409 project.ambiguous_product`\n</Update>\n\n<Update label='September 12, 2026' description='API changes'>\n **A live subscription no longer blocks `DELETE /workspace`.** The workspace goes and its subscription is canceled with the billing provider in the same call, immediately and with no refund for the rest of the period. The `409 workspace.delete_has_subscription` branch is gone and the code has been retired from the error vocabulary.\n\n - The cancellation is attempted once the workspace is removed, so a billing provider that refuses it leaves the subscription live rather than failing the delete. It is still cancellable from the customer portal, which any workspace's billing page opens\n - The billing recipients, meaning every member holding `billing:manage` or `workspace:update`, are emailed a confirmation naming the workspace and the canceled plan\n - The other four preconditions are unchanged: the owner only, never your last workspace, at least 7 days old, and no projects left\n</Update>\n\n<Update label='August 22, 2026' description='API changes'>\n **Envless no longer rate limits anything you do.** The per-invite resend cap and cooldown are gone, so `invites.resend()` never answers `429` and can be called as often as you need. Only a missing invite (`404 invite.not_found`) and an expired one (`409 invite.expired`) refuse it. The matching caps on `members.invite()`, which used to refuse a repeat invitation to an address already holding a pending invite, are gone too.\n\n - `EnvlessApiError.isRateLimited` and `retryAfterSeconds` stay, and the client still retries a `429` with backoff. A `429` can now only have come from infrastructure in front of the API, such as a CDN or a proxy\n - `resendCount` and `lastResendAt` are still returned on `InviteResource`, but they are bookkeeping rather than a budget\n</Update>\n\n<Update label='July 30, 2026' description='API additions'>\n These public API endpoints were added so the SDK could reach everything the CLI can. They are available over plain HTTP too.\n\n - `GET /workspaces` and `POST /workspaces`, the first endpoints that read the key owner's memberships rather than the bound workspace\n - `DELETE /workspace`\n - `PATCH /workspace` now accepts `virtualEnvsEnabled`\n - `GET`, `POST` and `DELETE /workspace/domain`, plus `POST /workspace/domain/verify`\n - `POST /projects/{slug}/environments/{environmentSlug}/expose`\n - `DELETE /members/me`\n</Update>",p=[{depth:2,slug:"october-5-2026",text:"October 5, 2026"},{depth:2,slug:"september-12-2026",text:"September 12, 2026"},{depth:2,slug:"august-22-2026",text:"August 22, 2026"},{depth:2,slug:"july-30-2026",text:"July 30, 2026"}],c={"api/overview":{title:"Overview"},"api/authentication":{title:"Authentication"},"api/encryption":{title:"Encrypting Values"},"api/errors":{title:"Errors"}
1,"api/sdk":{title:"Official SDKs"},"api/endpoints/get-me":{title:"Get the calling identity"},"api/endpoints/create-workspace":{title:"Create a workspace"},"api/endpoints/delete-workspace":{title:"Delete the current workspace"},"api/endpoints/get-current-workspace":{title:"Get the current workspace"},"api/endpoints/list-workspaces":{title:"List your workspaces"},"api/endpoints/update-workspace":{title:"Update the current workspace"},"api/endpoints/claim-custom-domain":{title:"Claim a custom domain"},"api/endpoints/get-custom-domain":{title:"Get the custom domain"},"api/endpoints/remove-custom-domain":{title:"Remove the custom domain"},"api/endpoints/verify-custom-domain":{title:"Verify the custom domain"},"api/endpoints/delete-member":{title:"Remove a member"},"api/endpoints/invite-member":{title:"Invite a member"},"api/endpoints/leave-workspace":{title:"Leave the workspace"},"api/endpoints/list-members":{title:"List workspace members"},"api/endpoints/update-member":{title:"Change a member's role"},"api/endpoints/list-invites":{title:"List pending invites"},"api/endpoints/resend-invite":{title:"Resend an invite email"},"api/endpoints/revoke-invite":{title:"Revoke an invite"},"api/endpoints/create-role":{title:"Create a custom role"},"api/endpoints/delete-role":{title:"Delete a custom role"},"api/endpoints/get-role":{title:"Get a role"},"api/endpoints/list-permissions":{title:"List the permission catalogue"},"api/endpoints/list-roles":{title:"List roles"},"api/endpoints/update-role":{title:"Update a custom role"},"api/endpoints/create-product":{title:"Create a product"},"api/endpoints/delete-product":{title:"Delete a product"},"api/endpoints/duplicate-product":{title:"Duplicate a product"},"api/endpoints/get-product":{title:"Get a product"},"api/endpoints/list-product-projects":{title:"List a product's projects"},"api/endpoints/list-products":{title:"List products"},"api/endpoints/update-product":{title:"Update a product"},"api/endpoints/grant-product-access":{title:"Grant product access"},"api/endpoints/list-product-access":{title:"List product access grants"},"api/endpoints/revoke-product-access":{title:"Revoke product access"},"api/endpoints/create-project":{title:"Create a project"},"api/endpoints/delete-project":{title:"Delete a project"},"api/endpoints/duplicate-project":{title:"Duplicate a project"},"api/endpoints/get-project":{title:"Get a project"},"api/endpoints/list-projects":{title:"List projects"},"api/endpoints/update-project":{title:"Update a project"},"api/endpoints/create-environment":{title:"Create an environment"},"api/endpoints/delete-environment":{title:"Delete an environment"},"api/endpoints/duplicate-environment":{title:"Duplicate an environment"}
1,"api/endpoints/expose-environment":{title:"Expose or unexpose an environment"},"api/endpoints/get-environment":{title:"Get an environment"},"api/endpoints/list-environments":{title:"List a project's environments"},"api/endpoints/update-environment":{title:"Update an environment"},"api/endpoints/apply-variables":{title:"Apply a set of variable changes atomically"},"api/endpoints/create-variable":{title:"Create a variable"},"api/endpoints/create-variables":{title:"Create variables in bulk"},"api/endpoints/delete-variable-by-id":{title:"Delete a variable by id"},"api/endpoints/delete-variable-by-name":{title:"Delete a variable by name"},"api/endpoints/delete-variables":{title:"Delete variables by name"},"api/endpoints/get-variable":{title:"Get a variable by id"},"api/endpoints/get-variable-by-name":{title:"Get a variable by name"},"api/endpoints/get-variable-history":{title:"List a variable's history"},"api/endpoints/list-variables":{title:"List variables"},"api/endpoints/update-variable-by-id":{title:"Update a variable by id"},"api/endpoints/update-variable-by-name":{title:"Update a variable by name"},"api/endpoints/create-version":{title:"Create a version"},"api/endpoints/get-version":{title:"Get a version"},"api/endpoints/list-version-entries":{title:"List version entry ciphertext"},"api/endpoints/list-versions":{title:"List versions"},"api/endpoints/reencrypt-version-entries":{title:"Write re-encrypted version entry ciphertext"},"api/endpoints/rollback-version":{title:"Roll back to a version"},"api/endpoints/create-key":{title:"Create an API key"},"api/endpoints/delete-key":{title:"Revoke an API key"},"api/endpoints/get-key":{title:"Get an API key"},"api/endpoints/list-keys":{title:"List API keys"},"api/endpoints/update-key":{title:"Update an API key"},"api/endpoints/create-webhook":{title:"Create a webhook endpoint"},"api/endpoints/delete-webhook":{title:"Delete a webhook endpoint"},"api/endpoints/get-delivery":{title:"Get a webhook delivery"},"api/endpoints/get-webhook":{title:"Get a webhook endpoint"},"api/endpoints/list-deliveries":{title:"List webhook deliveries"},"api/endpoints/list-event-types":{title:"List webhook event types"},"api/endpoints/list-webhooks":{title:"List webhook endpoints"},"api/endpoints/redeliver-webhook":{title:"Redeliver a webhook delivery"},"api/endpoints/rotate-webhook-secret":{title:"Rotate a webhook signing secret"},"api/endpoints/update-webhook":{title:"Update a webhook endpoint"},"webhooks/overview":{title:"Webhooks Overview"},"webhooks/events":{title:"Event Types"},"webhooks/events/workspace/created":{title:"workspace.created"},"webhooks/events/workspace/updated":{title:"workspace.updated"},"webhooks/events/workspace/deleted":{title:"workspace.deleted"},"webhooks/events/product/created":{title:"product.created"},"webhooks/events/product/updated":{title:"product.updated"},"webhooks/events/product/deleted":{title:"product.deleted"},"webhooks/events/project/created":{title:"project.created"},"webhooks/events/project/updated":{title:"project.updated"},"webhooks/events/project/deleted":{title:"project.deleted"},"webhooks/events/environment/created":{title:"environment.created"},"webhooks/events/environment/updated":{title:"environment.updated"},"webhooks/events/environment/deleted":{title:"environment.deleted"},"webhooks/events/environment/exposed":{title:"environment.exposed"},"webhooks/events/environment/unexposed":{title:"environment.unexposed"},"webhooks/events/environment/example-enabled":{title:"environment.example_enabled"},"webhooks/events/environment/example-disabled":{title:"environment.example_disabled"},"webhooks/events/variable/created":{title:"variable.created"},"webhooks/events/variable/updated":{title:"variable.updated"},"webhooks/events/variable/deleted":{title:"variable.deleted"},"webhooks/events/variable/bulk_created":{title:"variable.bulk_created"},"webhooks/events/variable/bulk_deleted":{title:"variable.bulk_deleted"},"webhooks/events/variable/bulk_updated":{title:"variable.bulk_updated"},"webhooks/events/variable/applied":{title:"variable.applied"},"webhooks/events/version/created":{title:"variable_version.created"},"webhooks/events/version/rolled_back":{title:"variable_version.rolled_back"},"webhooks/events/member/invited":{title:"member.invited"},"webhooks/events/member/invite_resent":{title:"member.invite_resent"}
1,"webhooks/events/member/invite_revoked":{title:"member.invite_revoked"},"webhooks/events/member/accepted":{title:"member.accepted"},"webhooks/events/member/updated":{title:"member.updated"},"webhooks/events/member/removed":{title:"member.removed"},"webhooks/events/key/created":{title:"key.created"},"webhooks/events/key/updated":{title:"key.updated"},"webhooks/events/key/rotated":{title:"key.rotated"},"webhooks/events/key/deleted":{title:"key.deleted"},"webhooks/events/cli_token/created":{title:"cli_token.created"},"webhooks/events/cli_token/revoked":{title:"cli_token.revoked"},"webhooks/events/role/created":{title:"role.created"},"webhooks/events/role/updated":{title:"role.updated"},"webhooks/events/role/deleted":{title:"role.deleted"},"webhooks/events/subscription/created":{title:"subscription.created"},"webhooks/events/subscription/updated":{title:"subscription.updated"},"webhooks/events/webhook/created":{title:"webhook.created"},"webhooks/events/webhook/updated":{title:"webhook.updated"},"webhooks/events/webhook/deleted":{title:"webhook.deleted"},"webhooks/events/webhook/secret_rotated":{title:"webhook.secret_rotated"},"webhooks/events/webhook/delivery_failed":{title:"webhook.delivery_failed"},"webhooks/signing":{title:"Signature Verification"},"webhooks/managing":{title:"Managing Webhooks"},"api/changelog":{title:"API Changelog"}},s=[];function h(){return s}function o(n){const t={a:"a",code:"code",li:"li",p:"p",strong:"strong",ul:"ul",...n.components},{Update:i}=t;return i||a("Update"),e.jsxs(e.Fragment,{children:[e.jsxs("p",{children:["Version history for the Envless HTTP API. The TypeScript client over it ships separately as ",e.jsx(t.code,{children:"@goenvless/sdk"}),", the Python and Ruby clients as ",e.jsx(t.code,{children:"envless-sdk"}),", the PHP client as ",e.jsx(t.code,{children:"envless/sdk"}),", and each has its own changelog, ",e.jsx(t.a,{href:"/docs/sdk/changelog",children:"TypeScript"}),", ",e.jsx(t.a,{href:"/docs/python/changelog",children:"Python"}),", ",e.jsx(t.a,{href:"/docs/ruby/changelog",children:"Ruby"})," and ",e.jsx(t.a,{href:"/docs/php/changelog",children:"PHP"}),". The ",e.jsx(t.code,{children:"envless"})," CLI ships as ",e.jsx(t.code,{children:"@goenvless/cli"})," and the application runtime as ",e.jsx(t.code,{children:"@goenvless/env"}),"."]}),` 2`,e.jsxs(i,{label:"October 5, 2026",description:"API fixes",children:[e.jsxs(t.p,{children:[e.jsx(t.strong,{children:"Project lookups now answer with the codes this reference documents."})," Seventeen routes that take a project slug answered an ambiguous slug with the generic ",e.jsx(t.code,{children:"409 request.conflict"})," and a missing project with ",e.jsx(t.code,{children:"404 request.not_found"}),". They now send ",e.jsx(t.code,{children:"409 project.ambiguous_product"})," and ",e.jsx(t.code,{children:"404 project.not_found"}),", like the routes that already did. The statuses are unchanged, so code that branches on them keeps working."]}),e.jsxs(t.ul,{children:[` 3`,e.jsx(t.li,{children:"Affected: reading, updating, deleting and exposing environments, reading, updating and deleting projects, every variable route under an environment except single create and apply, and listing and publishing versions"}),` 4`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"POST .../expose"})," answered an ambiguous slug with ",e.jsx(t.code,{children:"409 project.not_found"}),", and now sends ",e.jsx(t.code,{children:"409 project.ambiguous_product"})]}),` 5`]})]}),` 6`,e.jsxs(i,{label:"September 12, 2026",description:"API changes",children:[e.jsxs(t.p,{children:[e.jsxs(t.strong,{children:["A live subscription no longer blocks ",e.jsx(t.code,{children:"DELETE /workspace"}),"."]})," The workspace goes and its subscription is canceled with the billing provider in the same call, immediately and with no refund for the rest of the period. The ",e.jsx(t.code,{children:"409 workspace.delete_has_subscription"})," branch is gone and the code has been retired from the error vocabulary."]}),e.jsxs(t.ul,{children:[` 7`,e.jsx(t.li,{children:"The cancellation is attempted once the workspace is removed, so a billing provider that refuses it leaves the subscription live rather than failing the delete. It is still cancellable from the customer portal, which any workspace's billing page opens"}),` 8`,e.jsxs(t.li,{children:["The billing recipients, meaning every member holding ",e.jsx(t.code,{children:"billing:manage"})," or ",e.jsx(t.code,{children:"workspace:update"}),", are emailed a confirmation naming the workspace and the canceled plan"]}),` 9`,e.jsx(t.li,{children:"The other four preconditions are unchanged: the owner only, never your last workspace, at least 7 days old, and no projects left"}),` 10`]})]}),` 11`,e.jsxs(i,{label:"August 22, 2026",description:"API changes",children:[e.jsxs(t.p,{children:[e.jsx(t.strong,{children:"Envless no longer rate limits anything you do."})," The per-invite resend cap and cooldown are gone, so ",e.jsx(t.code,{children:"invites.resend()"})," never answers ",e.jsx(t.code,{children:"429"})," and can be called as often as you need. Only a missing invite (",e.jsx(t.code,{children:"404 invite.not_found"}),") and an expired one (",e.jsx(t.code,{children:"409 invite.expired"}),") refuse it. The matching caps on ",e.jsx(t.code,{children:"members.invite()"}),", which used to refuse a repeat invitation to an address already holding a pending invite, are gone too."]}),e.jsxs(t.ul,{children:[` 12`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"EnvlessApiError.isRateLimited"})," and ",e.jsx(t.code,{children:"retryAfterSeconds"})," stay, and the client still retries a ",e.jsx(t.code,{children:"429"})," with backoff. A ",e.jsx(t.code,{children:"429"}
12)," can now only have come from infrastructure in front of the API, such as a CDN or a proxy"]}),` 13`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"resendCount"})," and ",e.jsx(t.code,{children:"lastResendAt"})," are still returned on ",e.jsx(t.code,{children:"InviteResource"}),", but they are bookkeeping rather than a budget"]}),` 14`]})]}),` 15`,e.jsxs(i,{label:"July 30, 2026",description:"API additions",children:[e.jsx(t.p,{children:"These public API endpoints were added so the SDK could reach everything the CLI can. They are available over plain HTTP too."}),e.jsxs(t.ul,{children:[` 16`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"GET /workspaces"})," and ",e.jsx(t.code,{children:"POST /workspaces"}),", the first endpoints that read the key owner's memberships rather than the bound workspace"]}),` 17`,e.jsx(t.li,{children:e.jsx(t.code,{children:"DELETE /workspace"})}),` 18`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"PATCH /workspace"})," now accepts ",e.jsx(t.code,{children:"virtualEnvsEnabled"})]}),` 19`,e.jsxs(t.li,{children:[e.jsx(t.code,{children:"GET"}),", ",e.jsx(t.code,{children:"POST"})," and ",e.jsx(t.code,{children:"DELETE /workspace/domain"}),", plus ",e.jsx(t.code,{children:"POST /workspace/domain/verify"})]}),` 20`,e.jsx(t.li,{children:e.jsx(t.code,{children:"POST /projects/{slug}/environments/{environmentSlug}/expose"})}),` 21`,e.jsx(t.li,{children:e.jsx(t.code,{children:"DELETE /members/me"})}),` 22`]})]})]})}function v(n={}){const{wrapper:t}=n.components||{};return t?e.jsx(t,{...n,children:e.jsx(o,{...n})}):o(n)}function a(n,t){throw new Error("Expected component `"+n+"` to be defined: you likely forgot to import, pass, or provide it.")}export{l as body,v as default,d as frontmatter,h as getHeadings,s as headings,c as navTitles,p as updates};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.