> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables and context

> Data management throughout workflow execution

Every workflow execution maintains data in four organized namespaces. This data flows between nodes, gets updated by user interactions, and drives workflow logic.

## Data structure

**`vars` namespace** - User-defined variables

* Read/write access
* Store custom data, user responses, API results
* Persists throughout workflow execution

**`system` namespace** - System-managed data

* Read-only access
* Workflow metadata, timing, execution details
* Automatically maintained by the platform

**`context` namespace** - Contextual information

* Mostly read-only (set at workflow start)
* Channel info, user details, trigger context
* Provides execution environment details

**`metadata` namespace** - Request metadata

* Read-only access
* API request details, timestamps, caller info
* Available for API-triggered workflows

## Initial data

**WhatsApp trigger workflow starts with:**

```
# System variables
{{system.trigger_type}}                # "inbound_message"
{{system.workflow_id}}                 # Workflow UUID
{{system.started_at}}                  # ISO timestamp

# WhatsApp config data
{{system.whatsapp_config.id}}          # WhatsApp config ID
{{system.whatsapp_config.phone_number_id}}  # Meta phone number ID
{{system.whatsapp_config.display_phone_number}}  # Host phone number (formatted)
{{system.whatsapp_config.display_phone_number_normalized}}  # Host phone (normalized)
{{system.whatsapp_config.business_account_id}}  # WhatsApp Business Account ID
{{system.whatsapp_config.name}}        # Config name
{{system.whatsapp_config.kind}}        # "production" or "sandbox"

# Customer data (when WhatsApp config is linked to a Customer)
{{system.customer.id}}                 # Customer UUID
{{system.customer.external_customer_id}}  # Your external customer ID
{{system.customer.name}}               # Customer name

# Context variables
{{context.phone_number}}               # User's phone number "+1234567890"
{{context.whatsapp_business_scoped_user_id}}  # User BSUID (may be null)
{{context.whatsapp_parent_business_scoped_user_id}}  # Parent BSUID (may be null)
{{context.whatsapp_username}}          # WhatsApp username (may be null)
{{context.channel}}                    # "whatsapp"
{{context.conversation_id}}            # WhatsApp conversation ID

# Contact data
{{context.contact.id}}                 # Contact UUID (when known)
{{context.contact.wa_id}}              # Normalized WhatsApp ID
{{context.contact.business_scoped_user_id}}  # User BSUID (may be null)
{{context.contact.parent_business_scoped_user_id}}  # Parent BSUID (may be null)
{{context.contact.username}}           # WhatsApp username (may be null)
{{context.contact.name}}               # Contact name (or phone number fallback)
{{context.contact.profile_name}}       # WhatsApp profile name (may be null)
{{context.contact.display_name}}       # Display name (may be null)

# Workflow variables
{{last_user_input}}                    # Initial message content
```

**API trigger workflow starts with:**

```
# System variables
{{system.trigger_type}}                     # "api_call"
{{system.tracking_id}}                      # Unique execution ID
{{system.api_key_id}}                       # API key used
{{system.workflow_id}}                      # Workflow UUID
{{system.trigger_whatsapp_config_id}}       # WhatsApp config ID (if provided in request)

# Context variables
{{context.phone_number}}                    # From request (normalized)
{{context.channel}}                         # "api"

# Custom variables (from API request)
{{your_variable}}                           # From variables object
{{another_variable}}                        # From variables object

# Metadata
{{metadata.request_ip}}                     # Caller IP
{{metadata.request_timestamp}}              # Request time
```

**WhatsApp event trigger workflow starts with:**

```
# System variables
{{system.trigger_type}}                     # "whatsapp_event"
{{system.observer_mode}}                    # true
{{system.allow_outbound}}                   # false
{{system.workflow_id}}                      # Workflow UUID
{{system.started_at}}                       # ISO timestamp

# Event data
{{system.event.type}}                         # Event type (e.g. "whatsapp.message.sent")
{{system.event.payload}}                      # Full event payload

# Conversation snapshot
{{system.event.conversation.id}}              # Conversation ID
{{system.event.conversation.phone_number}}    # Customer phone "+1234567890"
{{system.event.conversation.business_scoped_user_id}}  # User BSUID (may be null)
{{system.event.conversation.parent_business_scoped_user_id}}  # Parent BSUID (may be null)
{{system.event.conversation.username}}        # WhatsApp username (may be null)
{{system.event.conversation.status}}          # "active" or "ended"
{{system.event.conversation.phone_number_id}} # Meta phone number ID

# Message snapshot (for message events)
{{system.event.message.id}}                   # WhatsApp message ID
{{system.event.message.type}}                 # "text", "image", "video", etc.
{{system.event.message.timestamp}}            # Unix timestamp
{{system.event.message.from_user_id}}         # Inbound BSUID when available
{{system.event.message.to_user_id}}           # Outbound BSUID when available
{{system.event.message.username}}             # WhatsApp username when available
{{system.event.message.text.body}}            # Message text (when applicable)
{{system.event.message.kapso.direction}}      # "inbound" or "outbound"
{{system.event.message.kapso.status}}         # Message status
```

When `phone_number_id` is provided in the API request:

* Automatically stored in `{{system.trigger_whatsapp_config_id}}`
* Phone number normalized and stored in `{{context.phone_number}}`
* Guarantees all messages use the same WhatsApp configuration

## Accessing variables

Use `{{variable_name}}` syntax in messages, templates, and AI fields to access variables.

**Direct access** (looks in vars namespace):

```
Hello {{customer_name}}
```

**Explicit namespace access**:

```
Your number is {{context.phone_number}}
Workflow started at {{system.started_at}}
```

## Resume tracking

After a workflow resumes from a wait step, these system variables are available:

```
{{system.last_resume.reason}}          # "user_input" or "timeout"
{{system.last_resume.at}}              # When workflow resumed (ISO timestamp)
{{system.last_resume.step_id}}         # Step ID where workflow was waiting
{{system.last_resume.step_identifier}} # Step identifier where workflow was waiting
```

Use `{{system.last_resume.reason}}` to detect if a wait step timed out:

```javascript theme={null}
// In a decide node or function
if (system.last_resume.reason === "timeout") {
  // Handle timeout scenario
} else {
  // Handle normal user response
}
```

While a wait step with timeout is active, these variables are available:

```
{{system.pending_timeout.job_id}}         # Background job ID
{{system.pending_timeout.timeout_seconds}} # Timeout duration
{{system.pending_timeout.scheduled_for}}   # When timeout will fire (ISO timestamp)
{{system.pending_timeout.step_id}}        # Step the timeout applies to
```

## Data flow between nodes

**Send nodes** (SendTextNode, SendTemplateNode, SendInteractiveNode)

* **Read**: All variables for message content and parameters
* **Write**: Nothing

**Wait for response node**

* **Read**: Nothing (just waits)
* **Write**: Sets `{{last_user_input}}` when user responds (not set on timeout). Sets `{{system.last_resume}}` metadata on resume. Sets `{{system.pending_timeout}}` when timeout is scheduled

**Decide node**

* **Read**: All variables to evaluate conditions
* **Write**: Nothing (just routes workflow)

**Function node**

* **Read**: Sends entire execution context to function
* **Write**: Can set variables via `save_response_to` or function return

**Agent node**

* **Read**: Full access to all variables via `get_variable` tool
* **Write**: Can set any variable via `save_variable` tool

**Handoff node**

* **Read**: Nothing
* **Write**: Nothing (just stops execution)

## Variable naming

* Use lowercase with underscores: `user_name`, `order_total`
* Be descriptive: `last_user_input` not `input`
* Avoid system reserved names: `flow_id`, `started_at`

## Environment variables

Store sensitive values like API keys as environment variables. They use a distinct syntax to avoid conflicts with workflow variables.

**Syntax**: `${ENV:VARIABLE_NAME}`

Each variable has two values:

* **Development**: Used during test runs
* **Production**: Baked into published workflows

### Scopes

**Project-level** — Available across all workflows in the project.

**Access**: Project Settings → Environment variables (or click the key icon in the workflow canvas toolbar).

**Flow-level** — Scoped to a single workflow. Flow-level variables **override** project-level variables with the same key.

**Access**: Workflow Settings → Environment variables.

### Example usage

In webhook URLs:

```
https://api.example.com/v1/data?key=${ENV:API_KEY}
```

In agent MCP headers:

```
Authorization: Bearer ${ENV:MCP_API_KEY}
```

### Key differences from workflow variables

|        | Workflow variables | Environment variables    |
| ------ | ------------------ | ------------------------ |
| Syntax | `{{var_name}}`     | `${ENV:VAR_NAME}`        |
| Scope  | Single execution   | Project or flow          |
| Set by | Workflow runtime   | Settings                 |
| Values | Dynamic            | Static (per environment) |
