Variables and Secrets
Manage reusable values and secure credentials across your custom actions
Custom actions require a Builder plan or higher.
Upgrade to Builder →
Application Variables
Application variables are reusable values shared across all custom actions in your app.
Creating Variables
- Open any custom action
- Navigate to Variables tab
- Click Add Variable
| Field | Description | Example |
|---|---|---|
| Name | Variable identifier: letters, numbers, and underscores, not starting with a number (UPPER_SNAKE_CASE recommended) | API_KEY |
| Label | Display name | API Key |
| Type | Data type | secret |
| Value | Actual value | sk-abc123... |
| Required | Marks a variable that must have a value | On |
Variable Types
| Type | Use Case | Storage |
|---|---|---|
| string | Text values, URLs | Plain text |
| number | Numeric values | Plain text |
| boolean | True/false flags | Plain text |
| secret | API keys, passwords | Encrypted |
| url | Base URLs, endpoints | Plain text with validation |
Using Variables
Reference variables with {{var.VARIABLE_NAME}} syntax:
https://{{var.API_DOMAIN}}/v{{var.API_VERSION}}/endpoint
Authorization: Bearer {{var.API_KEY}}
X-Workspace-ID: {{var.WORKSPACE_ID}}
{
"api_key": "{{var.API_KEY}}",
"environment": "{{var.ENVIRONMENT}}"
}Variable Resolution
Variables can be used in:
- Exact match: Entire value is the variable
- Embedded: Variable within a larger string
{{var.API_KEY}}
Bearer {{var.API_KEY}}
https://{{var.DOMAIN}}/api/v{{var.VERSION}}System Variables
System variables give your custom actions access to runtime context: information about the current conversation, user, and timing that the platform automatically provides. Unlike application variables (which you define), system variables are built-in and always available.
When to use system variables:
- Track which user triggered an action for audit logs
- Add timestamps to records you create via API
Available System Variables
| Variable | Type | Description |
|---|---|---|
{{system.user_id}} | String | Unique identifier for the current user |
{{system.consumer_id}} | String | Identifier of the end user (consumer) chatting with your app |
{{system.timestamp}} | String | Current time as an ISO 8601 string |
{{system.session_id}} | String | Unique identifier for the current chat session |
{{system.chat_transcript_url}} | String | Link to this conversation in the Chipp builder |
{{system.external_id}} | String | Opaque identifier passed via ?chipp_external_id= on the session URL |
{{system.query.NAME}} | String | Value of the NAME URL query param captured when the session started |
{{system.whatsapp_phone}} | String | The WhatsApp user’s phone number, for conversations that arrived over WhatsApp |
{{system.whatsapp_profile_name}} | String | The sender’s WhatsApp profile name from Meta. Empty when Meta omits it. User-controlled text |
User ID
What it is: A unique identifier for the person chatting with your agent.
Why use it: Track actions per user, create user-specific records, or implement per-user rate limiting in your backend.
Format: String (e.g., "user_abc123" or "clx9f2k...")
Example: Create a user-specific record
{
"user_id": "{{system.user_id}}",
"action": "submitted_feedback",
"data": "{{feedbackText}}"
}Example: Add to request headers for tracking
X-Chipp-User-ID: {{system.user_id}}The user ID is consistent across sessions for the same user, making it useful for building user-specific features in your integrations.
Timestamp
What it is: The current time when the action executes, as an ISO 8601 string in UTC.
Why use it: Add “created at” timestamps to records, implement time-based logic, or track when actions were triggered.
Format: ISO 8601 string (e.g., "2026-04-19T18:40:52.000Z")
Example: Log when a record was created
{
"created_at": "{{system.timestamp}}",
"user_id": "{{system.user_id}}",
"note": "{{noteContent}}"
}Example: Append timestamped data to Google Sheets
{
"values": [[
"{{system.timestamp}}",
"{{system.user_id}}",
"{{eventDescription}}"
]]
}The timestamp is an ISO 8601 string, not a number. If your API expects a numeric Unix timestamp, convert it on your backend.
Session ID
What it is: A unique identifier (UUID) for the current chat conversation.
Why use it: Track specific conversations, create links back to chats in your CRM, or log interactions with session-level granularity.
Format: String (UUID, e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")
Example: Send session reference to CRM
{
"session_id": "{{system.session_id}}",
"user_id": "{{system.user_id}}",
"lead_status": "{{leadStatus}}"
}Chat Transcript URL
What it is: A link to the current conversation in the Chipp builder.
Why use it: Include in webhook payloads so your team can click from your CRM, Slack, or ticketing system straight into the conversation.
Format: String (e.g., "https://build.chipp.ai/#/app/YOUR_APP_ID/conversations/3ace0bfc-6b75-...")
Example: Send the conversation link to a CRM via webhook
{
"lead_name": "{{leadName}}",
"transcript_link": "{{system.chat_transcript_url}}",
"session_id": "{{system.session_id}}"
}The link opens the conversation in the builder, so only people who can sign in to your Chipp workspace can view it. It does not return JSON. Use it for internal team tools, not for public-facing integrations.
No consumer OAuth tokens
There is no system variable for an end user’s OAuth token. A custom action cannot send the token a user created when they connected a hosted integration (for example their own Google or Salesforce account). To call an API as each end user, use Credential source: Collected from the user each session on the Auth tab, or use a built-in integration that supports per-user accounts.
External ID
What it is: An opaque identifier bound to the chat session. The platform maintains it as a first-class field on the session record (chat.sessions.external_id), so it is reliably available throughout the conversation. You can set it via query param (client-driven) or Builder API (server-driven). Both paths populate the same field.
Why use it: Bind a chat session to a known entity in your system (a patient record, a lead ID, a campaign reference) without requiring account creation. Your backend receives the identifier in the custom action payload and can look up the record it references.
Format: String (colon-free, up to 1024 characters, e.g., "lead_abc123", "patient-987")
How to set it (two sources, same session-level field):
-
Query param (client-driven). Append
?chipp_external_id=<your-value>to any chat link or embed URL:plaintexthttps://your-app.on.chipp.ai?chipp_external_id=patient-987 -
POST /consumers/auth(server-driven). If you bridge your own backend auth into Chipp via the Builder API, passexternal_idin the request body. The value is bound to the auth session minted for that call, and every chat session subsequently created under the returned bearer token (viaChippWidget.setUser({ token })) inherits it automatically, with no query param needed.bashcurl -X POST https://build.chipp.ai/api/v1/apps/YOUR_APP_ID/consumers/auth \ -H "Authorization: Bearer chipp_YOUR_API_KEY" \ -d '{"email": "jane@example.com", "external_id": "patient-987"}'
Precedence: If both a query param AND a /consumers/auth external_id are present for the same session, the explicit request-level value (from /consumers/auth) wins over an inbound query-param promotion. Either way, the value lands in the SAME field (chat.sessions.external_id). There is no separate durable “consumer-level” external ID.
Example: Bind a session to a CRM record
{
"external_id": "{{system.external_id}}",
"session_id": "{{system.session_id}}",
"event": "chat_completed"
}Promotion rules. Values containing : (colon) are NOT promoted to the first-class external_id field, even if you set them. This guard protects internal channel identifiers (e.g. whatsapp:, voice:) from collision. If your value contains a colon:
- It will NOT appear in
{{system.external_id}} - Use
{{system.query.chipp_external_id}}to read it instead - To use it in custom actions, bind an alternate query param that is colon-free
Values must also be colon-free when passed to /consumers/auth. Invalid values are rejected with a 400 error.
URL Query Parameters
What it is: A sub-namespace that gives custom actions deterministic access to URL query parameters captured when the consumer’s session started. Replace NAME with the query parameter key you want.
Why use it: Pass attribution tokens, variant IDs, or any other value embedded in your link directly into a custom action header or body. The platform resolves the value at request time from the stored session record, so the model never has to copy the raw value from the conversation.
Format: String (resolves to empty string if the session has no such param)
How it works: If a visitor arrives at:
https://your-app.on.chipp.ai?rp_token=abc123&ab_variant=Bthen {{system.query.rp_token}} resolves to abc123 and {{system.query.ab_variant}} resolves to B in any custom action triggered during that session. The agent still decides when to call the action; the platform guarantees the value.
Example: Pass an attribution token in a header
Authorization: Bearer {{system.query.rp_token}}Example: Forward a variant and session reference to an analytics endpoint
{
"variant": "{{system.query.ab_variant}}",
"external_id": "{{system.external_id}}",
"session_id": "{{system.session_id}}"
}Encoding: Values are URL-encoded when used in the base URL (injection guard), and passed as-is in headers and body fields.
Reserved keys that are never captured as query params: bt, session, share, magic_link, q, widget, chat_session. These are used internally by Chipp and always resolve to an empty string.
WhatsApp Phone
What it is: The phone number of the user chatting with your agent over WhatsApp, sourced from the same canonical channel metadata that Sheets export renders as its own field.
Why use it: Populate a phone field in an outbound webhook (e.g. an N8N or CRM payload) without asking the model to copy the number out of the conversation text.
Format: String, digits only, no + or formatting (e.g. "15551234567")
Example: Send the WhatsApp phone number to N8N
{
"phone": "{{system.whatsapp_phone}}",
"session_id": "{{system.session_id}}",
"message": "{{messageText}}"
}This variable resolves to an empty string for any conversation that did not arrive over WhatsApp (web chat, email, Slack, etc.), and for a WhatsApp conversation where the phone metadata is genuinely unavailable. It never falls back to another channel’s identifier.
WhatsApp Profile Name
What it is: The sender’s WhatsApp profile name, exactly as Meta reports it in the inbound webhook (contacts[].profile.name). The exact variable name is {{system.whatsapp_profile_name}}.
Why use it: Greet the person by name or fill a name field in a CRM or N8N payload without asking the model to guess it from the conversation.
Format: String, free text (e.g. "Example User"). It can contain spaces, emoji, and non-Latin characters.
Example: Send the WhatsApp profile name to N8N
{
"phone": "{{system.whatsapp_phone}}",
"name": "{{system.whatsapp_profile_name}}"
}Meta does not always send a profile name. When it is missing, this variable is an empty string, and the action still runs. It is also empty on any conversation that did not arrive over WhatsApp. Chipp never guesses a name from the message text or the phone number.
The profile name is user-controlled text. The sender chooses it, so treat it as untrusted input in your backend. Do not run it as code, and validate or escape it before you put it in SQL, HTML, or shell commands.
Combining System Variables
System variables work alongside application variables and AI-generated parameters:
{
"api_key": "{{var.API_KEY}}",
"session": "{{system.session_id}}",
"metadata": {
"user": "{{system.user_id}}",
"timestamp": "{{system.timestamp}}",
"query": "{{searchQuery}}"
}
}In this example:
{{var.API_KEY}}: Your stored API key (application variable){{system.session_id}}: Current chat session (system variable){{system.user_id}}: Current user (system variable){{system.timestamp}}: Current time (system variable){{searchQuery}}: Value the AI determines from context (AI-generated)
Security Best Practices
1. Use Secret Type for Sensitive Data
Mark sensitive values as secret:
Type: secret
Name: API_KEY
Value: sk-abc123...
Type: string
Name: API_KEY
Value: sk-abc123...2. Never Expose Secrets in URLs
https://api.example.com/auth?key={{var.API_KEY}}
Headers:
Authorization: Bearer {{var.API_KEY}}3. Scope Variables Appropriately
Variables are scoped to applications, not global:
- Each app has its own variable namespace
- No cross-app variable access
- User-specific isolation
4. Rotate Keys Regularly
Update variable values without changing actions:
- Go to Variables tab
- Update the value
- All actions automatically use new value
Common Patterns
Environment Configuration
Manage different environments with variables:
API_DOMAIN = api.dev.example.com
ENVIRONMENT = development
API_DOMAIN = api.example.com
ENVIRONMENT = productionMulti-Tenant Setup
Use variables for tenant-specific values:
WORKSPACE_ID = ws_123456
TENANT_KEY = tenant_abc
ORG_ID = org_789API Versioning
Control API versions centrally:
API_VERSION = v2
SCHEMA_VERSION = 2024-01-15
CLIENT_VERSION = 1.2.3Variable Management
Viewing Variable Usage
See which actions use a variable:
- Check before deleting variables
- Update impacts all actions
Import/Export
When exporting actions to collections:
- Variable definitions included
- Variable values NOT exported
- Recipients set their own values
Testing Variables
Use the action tester to verify resolution:
URL: {{var.BASE_URL}}/users
Header: Bearer {{var.API_KEY}}
URL: https://api.example.com/users
Header: Bearer sk-abc123...Placeholders and Parameter Sources
You can reference a variable two ways:
- Pick it as the parameter’s source. Set the source dropdown to Application variable and type the name, or to System variable and pick one from the list. The whole value comes from the variable.
- Type a placeholder into a fixed value. Use this when the variable is only part of the text, for example
Bearer {{var.API_KEY}}.
In a fixed value, the placeholders resolve like this:
{{var.API_KEY}} # Application variable (fails clearly if it does not exist)
{{system.user_id}} # System variable (fails clearly if the name is unknown)
{{API_KEY}} # Application variable if one has that name, otherwise sent as typedA placeholder with no namespace is not filled by the AI in a fixed value. To have the AI write a value, set the parameter’s source to AI fills it in.
The cURL importer is the one exception: when you import a cURL command, a value that is exactly {{name}} becomes an AI fills it in parameter, {{var.NAME}} becomes an Application variable parameter, and {{system.NAME}} becomes a System variable parameter when NAME is one listed in Available System Variables.
Troubleshooting
Variables Not Resolving
-
Check Variable Name
- Case-sensitive
- No spaces in names
- Correct namespace (
var.orsystem.)
-
Check Variable Definition
- Variable exists in Variables tab
- Has a value set
- Correct variable type
-
Check Syntax
- Double curly braces:
{{}} - No extra spaces:
{{var.NAME}} - Correct namespace prefix
- Double curly braces:
Secret Values Not Visible
Secrets are encrypted and never shown after saving:
- ✓ This is expected behavior
- ✓ Update value to change
- ✓ Use tester to verify working
Cross-Action Dependencies
Variables are resolved per action:
- Define once, use everywhere
- Changes apply immediately
- No action rebuild required