Ensemble Docs

Managing variables

Creating, overriding, updating and deleting tenant variables, and what the API returns for a secret.

The shape of a variable

FieldNotes
NameHow it is referenced: ${vars.NAME} or ${secrets.NAME}.
ValueWhat it resolves to.
SecretWhether it is a credential. Decides which prefix reaches it and how it is returned.
KeyOptional. The signing key this row overrides for. Absent means tenant-wide default.

Name and key together identify a row: at most one default per name, and at most one override per name per key. Both are enforced by the database rather than checked in the application, so two requests racing to create the same override cannot both win.

Naming

Names are referenced from tool configurations by hand, so they are part of an interface. Conventions that save trouble later:

  • Upper snake case, like an operating system environment variable. STRIPE_API_KEY, not stripeApiKey.
  • Name the service, not the tool. SALESFORCE_TOKEN can be shared by five tools; LEAD_LOOKUP_TOKEN suggests it cannot.
  • Do not encode the environment in the name. API_KEY holding different values in your dev and production tenants is the point. PROD_API_KEY in the dev tenant is a mistake waiting to be made.

Secret or not

The flag changes three things: which prefix resolves it, how it is stored, and what the API gives back.

varssecrets
Referenced as${vars.NAME}${secrets.NAME}
StoredAs givenEncrypted
Returned by the APIThe value********
Readable backYesNo. Not by any endpoint

A secret's real value leaves the database in exactly one direction: into the resolver, when a tool or channel actually uses it. No endpoint returns it, which means you cannot look a secret up later.

Keep the authoritative copy wherever you got it from, a password manager or the provider's own console. If you only have it here, you do not have it: the API answers ******** and the only recovery is to set a new value.

Mark anything credential-shaped as secret even when it feels harmless. A listing is the sort of thing that ends up in a screen recording or a support ticket, and the flag is what keeps the value out of it.

Overrides

Create the default first, then add overrides for the keys that need something else. An override is the same name with a signing key attached.

# The tenant-wide default
curl -X POST https://service.ensembleapp.dev/api/environments \
  -H 'Authorization: Bearer <jwt>' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "API_TOKEN", "value": "shared-token", "isSecret": true }'

# What one integrator gets instead
curl -X POST https://service.ensembleapp.dev/api/environments \
  -H 'Authorization: Bearer <jwt>' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "API_TOKEN", "value": "their-token", "isSecret": true, "keyId": "<signing key id>" }'

Reading, updating and deleting follow the same rule: name alone addresses the default, and ?keyId= addresses that key's override.

curl https://service.ensembleapp.dev/api/environments/API_TOKEN \
  -H 'Authorization: Bearer <jwt>'

curl 'https://service.ensembleapp.dev/api/environments/API_TOKEN?keyId=<signing key id>' \
  -H 'Authorization: Bearer <jwt>'

Omitting keyId always means the default. That is deliberate, so every caller written before overrides existed keeps addressing exactly what it used to.

The API

GET    /api/environments              Every variable: defaults and overrides
GET    /api/environments/{name}       The default, or ?keyId= for one override
POST   /api/environments              Create a default, or an override with keyId
PUT    /api/environments/{name}       Update the default, or ?keyId= for one override
DELETE /api/environments/{name}       Delete the default, or ?keyId= for one override

Deleting

Deleting an override leaves the default, and callers on that key fall back to it. That is usually what you want when an integrator stops needing special treatment.

Deleting the default leaves overrides in place and anything without one resolving to nothing. A tool whose header becomes an empty string tends to fail in the service you are calling rather than here, so delete defaults deliberately.

Nothing warns you that a variable is still referenced. References live inside tool and channel configurations as text, so there is no link from the variable to its users. Before deleting one, search your tool definitions for the name.

Rotating a credential

Update the value in place. Tools resolve variables per call, so the next call picks it up with nothing to redeploy and no tool version to publish.

That is the main reason to keep credentials here rather than in a tool: a rotation is one edit rather than a change to every definition that uses it, each of which would otherwise need a new version.

On this page