Download all docs
modifiers

Variable

The configuration knob you attach to other elements: a typed key-value setting — URL, count, flag, or JSON blob — that cascades nearest-wins down the element → app → circle → platform chain and injects into execution as both `{{ vars.slug }}` and an environment variable.

Working with it

Opening a Variable launches a code editor — its dedicated working surface.

How it appears

The same element type rendered as a definition, a circle instance, and a live workspace card.

Va
type

Variable

Define typed configuration values with environment overrides

modifiersmodifierdefinition

When to use / not

When to use

  • Pulling a URL, threshold, count, or feature flag out of code so it can be changed without editing the element that reads it.
  • Giving one setting a different value per environment — `development`, `staging`, `production` — from a single attached variable via overrides.
  • Sharing one config value across many elements, referenced consistently as `{{ vars.slug }}` rather than duplicated inline.
  • Holding an encrypted secret (API key, token) via `set_value` with sensitive=true, so the value lives vault-encrypted and never as plaintext in the spec.

When not to use

  • A reusable text/instruction template for an LLM — use prompt; variable is for configuration values, not authored prose.
  • Intercepting or transforming requests at runtime — variable has no middleware phase; it resolves once at cascade time. Reach for a request-phase modifier like rate-limit or filter-words.
  • A plain non-sensitive value you want mirrored to a public GitHub repo — variables are excluded from public-mirror pushes because they commonly carry secrets.

Topology

Attaches to another element as a modifier, shaping that element's behaviour rather than running on its own.

Properties

typestring
Data type of the variable value
value
The variable value (type depends on type field). Accepts any JSON-compatible value.
value_typestring
Data type of the value (alias for 'type' — either field is accepted)
default
Default value if not set
validationobject
Value validation rules
requiredboolean
Whether a value must be provided
sensitiveboolean
Whether to mask value in logs/UI
scopestring
Variable scope
overrideobject
Environment-specific overrides
dataobject
Initial configuration data (key-value pairs). Set at creation time; use set_value operation for runtime updates.

Capabilities

Defined for this element
  • Storage
  • Crypto
  • Observe

Operations

  • attachPOST
  • deleteDELETE
  • delete_valuePOST
  • detachPOST
  • getGET
  • get_attached_modifiersGET
  • get_valueGET
  • list_attachmentsGET
  • list_keysGET
  • set_valuePOST
  • updatePATCH

Ports

Inputs

  • valueconfig
  • typeconfig
  • environment_overridesconfig
  • secretconfig

Composition

Attaches
Referenced by

Errors / when it fails

Required variable must have a value or default
Fails unless: value is not None or default is not None
validation.pattern only applies to string value_type
Fails unless: value_type == 'string'
validation.min/max only apply to number value_type
Fails unless: value_type == 'number'

Validation rules

  • Sensitive variable with global scope - consider environment-specific values
  • Required variable without default - deployment will fail if not set

Variable (variable)

Category: modifiers | Form: | Symbol: Va

Define typed configuration values with environment overrides

Defines typed key-value configuration that is injectable into execution environments. Cascade strategy: nearest (NearestMap) — local variable values override inherited ones with the same name. Stored in resolved.variables as HashMap<String, Value>. Resolution order: element → app → circle → platform (most specific wins). Variables are environment_injectable — they appear as environment variables in actor execution contexts. No middleware phase — variables are resolved at cascade time, not per-request. Use variable for configuration values (API keys, thresholds, URLs); use prompt for text templates. Inherits standard ops (get, update, delete, attach, detach) — no custom operations.

Guide

Overview

A Variable stores plain-text configuration values that can be referenced by other resources. Variables are safe to commit to version control and are visible in the UI.

Why Variables Exist

  • Configuration: Store URLs, feature toggles, counts, settings
  • Environment-Specific: Different values per environment (dev, staging, prod)
  • Reusable: Reference from multiple resources using {{ vars.slug }}
  • Version Controlled: Changes tracked in Git

Directory Structure

api-base-url/
├── README.md              # Documentation
├── .triform/
│   ├── triform.yaml       # kind: control/variable
│   ├── contract.yaml      # Capabilities
│   ├── spec.yaml          # Value definition
│   └── schema.json        # Validation
└── examples/
    └── with-overrides.yaml

Creating a Variable

$ triform create control/variable api-base-url

Configuration

Basic Variable

kind: control/variable
slug: api-base-url

spec:
  type: string
  value: "https://api.example.com"
  description: "Base URL for API requests"

Variable Types

# String
spec:
  type: string
  value: "https://api.example.com"

# Number
spec:
  type: number
  value: 100

# Boolean
spec:
  type: boolean
  value: true

# Object (JSON)
spec:
  type: object
  value:
    key: "value"
    nested:
      setting: true

# Array
spec:
  type: array
  value:
    - "item1"
    - "item2"

Environment Overrides

Different values for different environments:

kind: control/variable
slug: api-base-url

spec:
  type: string
  value: "https://api.example.com"  # Default (production)

  overrides:
    development:
      value: "http://localhost:3000"
    staging:
      value: "https://staging.api.example.com"

Inheritance

Variables can inherit from parent scopes:

kind: control/variable
slug: api-base-url

spec:
  type: string
  inherit_from: "@org/api-base-url"  # Use org-level default

  # Override only in specific cases
  overrides:
    development:
      value: "http://localhost:3000"

Referencing Variables

In Resource Specs

kind: actor/action
slug: fetch-data

spec:
  runtime:
    env:
      API_URL: "{{ vars.api-base-url }}"
      MAX_RETRIES: "{{ vars.max-retries }}"

In Code (SDK)

from triform import variables

# Get variable value
api_url = variables.get("api-base-url")

# Get with default
max_retries = variables.get("max-retries", default=3)

# Get typed
config = variables.get_object("app-config")
import { variables } from '@triform/sdk';

const apiUrl = await variables.get('api-base-url');
const maxRetries = await variables.get('max-retries', { default: 3 });

Validation

Variables can have validation rules:

spec:
  type: string
  value: "https://api.example.com"

  validation:
    # String patterns
    pattern: "^https://.*"

    # String length
    min_length: 10
    max_length: 200

    # Allowed values
    enum:
      - "https://api.example.com"
      - "https://staging.api.example.com"
spec:
  type: number
  value: 100

  validation:
    min: 1
    max: 1000

SDK Usage

Python

from triform import variables

# Get single variable
api_url = variables.get("api-base-url")

# Get all variables for current environment
all_vars = variables.list()

# Check if variable exists
if variables.exists("feature-enabled"):
    # ...

# Get with metadata
var = variables.get_with_metadata("api-base-url")
print(var.value)
print(var.updated_at)
print(var.scope)

TypeScript

import { variables } from '@triform/sdk';

const apiUrl = await variables.get('api-base-url');
const allVars = await variables.list();

const varMeta = await variables.getWithMetadata('api-base-url');
console.log(varMeta.value, varMeta.updatedAt);

Variable vs Secret

AspectVariableSecret
StoragePlain text in GitEncrypted in Vault
VisibilityVisible in UI/logsNever shown
RotationManual onlyAutomatic support
AuditStandard loggingFull access logging
Use forURLs, counts, flagsAPI keys, passwords

Rule of thumb: If you’d be worried about the value appearing in a log file, use a Secret.

Runtime Behavior

PropertyValue
Cascadenearest — the closest attached variable map wins per key
Eval Ordern/a (no middleware phase; resolved at element configuration load time)
Phasen/a
Fail Actionn/a (missing variable references surface as configuration errors at startup)
Applies Toactors, frontend, data, external

Variables are environment-injectable: resolved values are made available as {{ vars.<slug> }} template substitutions and as environment variables during execution. They do not participate in the request middleware pipeline.

Files in This Resource

  • README.md - Documentation
  • .triform/triform.yaml - Metadata
  • .triform/contract.yaml - Capabilities
  • .triform/spec.yaml - Value definition

Relationships

  • Attaches to: circle, app, automation, python, javascript, ruby, rust-fn, go-fn, csharp, hitl, triformer, claude-code, codex, open-code, external-agent, lab, diagram, sql, document, vector, graph, timeseries, contacts, organizations, entity, schema, files, cookie-jar, view, spa, ssr, three-d, slack, teams, discord, email, matrix, mattermost, rocketchat, http, queue, websocket, schedule, platform-trigger, phone-number, linkedin, twitter, facebook, instagram, github, fortnox, platform, chromeless, user-browser, recorder, board, planning-board, sales-board, recruitment-board

Capabilities

  • typed: Typed configuration
  • env-override: Environment-specific overrides
  • secrets: Secret value encryption
  • injection: Injected as environment variable

Properties

PropertyTypeDefaultDescription
typestring"string"Data type of the variable value
valuestringThe variable value (type depends on type field). Accepts any JSON-compatible value.
value_typestring"string"Data type of the value (alias for ‘type’ — either field is accepted)
defaultstringDefault value if not set
validationobjectValue validation rules
requiredbooleanfalseWhether a value must be provided
sensitivebooleanfalseWhether to mask value in logs/UI
scopestring"global"Variable scope
overrideobjectEnvironment-specific overrides
dataobjectInitial configuration data (key-value pairs). Set at creation time; use set_value operation for runtime updates.
valuesobjectKey-value store for multiple named values (written by set_value operation)
vault_storedbooleanfalseInternal flag: whether value is encrypted in vault

Operations

attach

Post /ops/attach | Auth: Read

Attach this modifier to a target element

Attaches this modifier to a target element. The target_id must be a UUID of an existing element that supports this modifier type (check applies_to in definition.yaml). Priority controls evaluation order when multiple modifiers of the same type are attached — lower priority runs first. The attachment is stored in element_modifiers table. Cascade resolution runs at bond-time to merge this modifier into the target’s resolved config. Common mistake: attaching to an incompatible element type — check topology rules first.

delete

Delete /ops/delete | Auth: Admin

Delete element (soft delete)

Soft delete — sets state to ‘deleted’ but retains the record. Cannot delete elements that have children (has_no_bond precondition) or active runs. Requires admin auth and confirmation.

delete_value

Post /ops/delete_value | Auth: Write

Delete a variable value by key, environment, or base value

Removes a stored value. If key is provided, removes spec.values[key]. If environment is provided, removes spec.override[environment]. If neither, removes the base spec.value.

detach

Post /ops/detach | Auth: Read

Detach this modifier from a target element

Removes this modifier from a target element. Requires the target_id. Pervasive modifiers (audit, policy) can only be detached at the level they were originally attached — inherited pervasive modifiers cannot be detached by child elements. After detach, cascade resolution re-runs to remove this modifier’s effect from the resolved config.

get

Get /ops/get | Auth: Read

Get element details

Element is already resolved by the routing layer — this returns the cached element, not a fresh DB query. Use the path /api/{circle}/{slug} to address elements.

get_attached_modifiers

Get /ops/attached/{target_id} | Auth: Read

Get all modifiers attached to a target element

Lists all modifiers attached to a specific target element, including modifier_id, type, subcategory, and priority. Useful for debugging cascade resolution or understanding which policies apply to an element before invoking it.

get_value

Get /ops/get_value | Auth: Read

Get the variable’s current value (decrypts if sensitive)

Returns the current value. For sensitive variables, decrypts from the vault. Lookup cascade when an environment is specified: (1) spec.override[environment] for exact match, (2) spec.values[key] or spec.value as fallback. When the requested environment has no override, the response ALWAYS includes env_requested, env_found=false, fell_back_to_base=true, and available_environments so callers can detect the silent fallback. Environment names are arbitrary strings set by whoever called set_value — they are NOT validated against an enum. Use list_keys to discover which environments exist on a given variable.

list_attachments

Get /ops/targets | Auth: Read

List all elements this modifier is attached to

Returns all target elements where this modifier is currently applied. Shows target_id, target_type, priority, and cascade_policy.

list_keys

Get /ops/list_keys | Auth: Read

List all stored keys, environments, and whether a base value exists

Returns the list of named keys in spec.values, environment names in spec.override, and whether a base value (spec.value or vault-stored) exists.

set_value

Post /ops/set_value | Auth: Write

Set the variable’s value (vault-encrypted if sensitive)

Sets the variable’s value. When sensitive=true or value_type=secret, the value is encrypted via VaultService and stored in the secret_values table — the spec stores only a “[encrypted]” marker, never the plaintext. Non-sensitive variables store the value directly in spec.value. Use this operation instead of update for sensitive values.

update

Patch /ops/update | Auth: Write

Update element

Partial update — send only the fields you want to change. spec, name, and intention are all independently optional. spec MUST be a JSON object when present; deep-merged into the existing spec by default. Empty {"spec":{}} preserves existing spec content but still records a new version (no-op for content, not for version state). To clear/replace the entire spec wholesale send {"spec":{...},"deep":false}. List-typed spec fields use replace semantics (the patch list replaces the existing list, no array merging). Coordinates Git + DB writes. Slug cannot be changed after creation.

Error Codes

CodeClassRetryableDescription
VARIABLE_NOT_FOUNDnot_foundnoVariable does not exist
VARIABLE_TYPE_MISMATCHvalidationnoValue doesn’t match declared type

Lifecycle / runtime

Defined for this element

Execution model: sync

Observability

Defined for this element

Metrics

  • evaluation_count
  • rejection_count

Events

  • variable.evaluated
  • variable.rejected

Pricing / cost

Platform default

Operation costs

  • create: free
  • update: free
  • delete: free
  • get: free
  • list: free
  • invoke: 10000 micro-AU
  • tool_use: free

Set it up

Valuestring
The variable's value
Typestring
Type (alias)string
Alias for 'type'
Sensitivestring
Hide value in logs and UI