Skip to main content

Custom variables

A custom variable is a provider-, customer-, or location-scoped name → value pair that you define yourself and then reference from a template as {{<level>.custom.<name>}}. Where the built-in namespaces (provider.*, customer.*, location.*) expose a fixed set of system fields — id, name, network ids — custom variables let you carry your own values (a region code, a DNS suffix, a cost-centre tag) into every resource a template materializes, without hard-coding them into the template body.

Each level owns its own isolated .custom.* sub-namespace:

LevelSub-resource pathResolves asManaged by
Provider/providers/{providerId}/custom-variables{{provider.custom.<name>}}anyone with update on the Provider
Customer/customers/{customerId}/custom-variables{{customer.custom.<name>}}anyone with update on the Customer
Location/locations/{locationId}/custom-variables{{location.custom.<name>}}anyone with update on the Location

The three sub-namespaces are independent — a region set on a provider (provider.custom.region) is a different variable from a region set on one of its customers (customer.custom.region). A template placeholder names the level explicitly, so there is never any inheritance or shadowing between levels.

Managing custom variables

Each level exposes the same three-verb sub-resource. The body is always a flat name → value map (the customVariables object); there is no create/delete — the whole set is edited in place, so a level with no custom variables simply returns an empty map.

VerbSemantics
GETReturns the current name → value map (empty when none are set). Requires read on the parent resource.
PUTReplaces the whole set wholesale — any variable omitted from the body is removed, and an empty or omitted map clears them all. Requires update on the parent resource.
PATCHApplies a JSON Merge Patch (RFC 7386): a name in the body is added or updated, a name mapped to null is removed (a no-op if it does not exist), and names absent from the body are left untouched. Requires update on the parent resource.

Reading is gated on read of the parent resource; both write verbs are gated on update, so in practice only an admin of that provider, customer, or location can change its custom variables. All three verbs return the full resulting set, so a caller can read, edit, and write back without tracking state.

See the API reference for request/response examples and the full path table.

Naming and limits

Custom variables are validated on every PUT/PATCH; an invalid body is rejected with a 4xx and each offending entry is reported as its own violation.

RuleConstraint
Name characters1–64 characters of [A-Za-z0-9_-], starting with a letter or digit. A leading hyphen or underscore is rejected.
Name shapeA single segment — no dots. The .custom. infix is supplied by the level, so a name is just the trailing <name> in {{provider.custom.<name>}}.
ValueAny string up to 1000 characters. Values are stored verbatim and are never recursively rendered, so a value may not itself contain the {{ or }} template delimiters.
CountAt most 50 custom variables per level. A PATCH may send more entries than that (its null removal markers inflate the body), but the resulting stored set must still fit within 50.

Using custom variables in a template

A custom variable extends its level's built-in namespace: wherever a template field may reference {{provider.name}}, it may also reference {{provider.custom.region}}. Because custom variables are dynamic — added, changed, or removed on the entity long after a template is authored — validation at template create/update time is structural only: a {{<level>.custom.<name>}} placeholder is accepted as long as its level is in scope for that field, without checking that the variable currently exists.

The existence check is deferred to apply time, where rendering is strict. At apply, each in-scope entity's custom variables are merged into the catalog under its <level>.custom.* sub-space, alongside the built-in keys. A placeholder that resolves to a variable which is not set — a typo, or a variable that was removed since the template was written — fails the whole apply with a 4xx listing the offending variables, exactly like an unknown built-in variable. Nothing is ever emitted blank or as a literal {{…}}.

Availability follows the same rules as the built-in namespace at each level, so:

  • provider.custom.* is available to every templated field.
  • customer.custom.* is available only when a customer is in scope for the apply (a template applied directly under a provider, with no customer, leaves it unresolved).
  • location.custom.* is the one exception. Like the built-in location.*, it is available only to a location apply's connector fields, which render in a second phase after the Location exists — not to the Location's own name/description, which render first. Those name/description fields still receive provider.custom.* and customer.custom.*, so custom variables absolutely do reach a Location's name and description; only the Location's own location.custom.* values are out of scope there.

For the full field-by-field breakdown of which namespaces each templated property can reference, and the two-phase rendering order, see the location template Variable substitution and customer template Variable substitution sections.

Finding where a variable is used

Before removing or renaming a custom variable, you can check whether anything still references it. Each level exposes a usages endpoint:

GET /providers/{providerId}/custom-variables/{name}/usages
GET /customers/{customerId}/custom-variables/{name}/usages
GET /locations/{locationId}/custom-variables/{name}/usages

It returns the templates and connector models under that resource that reference the variable's {{<level>.custom.<name>}} placeholder in a templated field — a template's name/description, anywhere in a location template's connector blueprint, or anywhere in a connector model's applications blueprint. Each entry gives the referencing resource's type (customer-template, location-template, or connector-model), its id and name, and a self link to it. The list is empty when the variable is unused.

Results are limited to the resources the caller is allowed to read, and the endpoint itself requires read on the parent resource. Because the match is a substring probe over the stored template text, it catches a reference wherever the placeholder appears, not only in fields a UI would surface as editable.

More info