Skip to main content

Environments & Secrets

[API Docs] [SDK]

Environments & Secrets is an organization-level key-value store for configuration and credentials. Define a variable once, then reference it anywhere using {{ env.variable_key }} syntax. Values are resolved at runtime, so the same configuration works across sandbox and production without changes.

You can manage your environment variables in the epilot portal under Configuration > Environments.

Why use environment variables?โ€‹

Without environment variables, credentials and URLs are stored directly inside webhooks, templates, and integrations. This causes problems:

  • Duplication -- The same API key appears in dozens of webhooks. When you rotate it, you must update every one.
  • Blueprint sync issues -- Blueprints copy configuration between sandbox and production. Hardcoded sandbox URLs end up in production, breaking integrations.
  • Security risk -- Credentials scattered across configs are harder to audit and easier to leak.

Environment variables solve all three. Store your credentials and URLs as variables, reference them with {{ env.* }}, and each organization resolves them to its own values.

Variable typesโ€‹

TypeDescriptionReadable?
StringPlain text value. Use for URLs, IDs, feature flags, and other non-sensitive config.Yes -- value is visible in the UI and returned by the API.
SecretStringEncrypted value. Use for API keys, OAuth secrets, passwords, and tokens.No -- value is write-only. Once saved, it cannot be read back through the UI or API.

Naming your variablesโ€‹

Variable keys use dot-separated namespacing to keep things organized:

erp_api.base_url
erp_api.oauth_token_url
erp_api.oauth_secret
portal.domain
n8n.metering_webhook_url

Rules:

  • Start with a lowercase letter or digit
  • Use only lowercase letters, digits, dots (.), underscores (_), and hyphens (-)
  • Maximum 128 characters
  • Pattern: ^[a-z0-9][a-z0-9_.-]{0,127}$
tip

Use the dot prefix to group related variables. For example, prefix all ERP-related variables with erp_api. so they appear together in the UI.

Referencing variablesโ€‹

Use double curly braces with the env namespace to reference a variable:

{{ env.erp_api.base_url }}

Spaces around the key are optional -- {{ env.key }} and {{env.key}} both work.

Where you can use {{ env.* }}โ€‹

Environment variables are supported in the following contexts:

ContextExample use
WebhooksURL, headers, authentication credentials (Basic Auth, API Key, OAuth)
Automation flowsCustom action payloads, external integration parameters
Email & document templatesPortal URLs, shared links, environment-specific content
Entity mappingTransformation rules, target URLs
ERP integrationsBase URLs, OAuth endpoints, file proxy configuration
Customer portalHook execution, link resolution, template variables

Resolution rulesโ€‹

  • Variables are resolved server-side at runtime. The {{ env.* }} placeholder is replaced with the actual value just before the action executes.
  • SecretString values are only resolved in trusted backend contexts (webhooks, integrations, automation actions). They are never sent to the browser or included in template previews.
  • If a referenced variable does not exist, the placeholder remains unresolved and the action fails with an error.

Working with Blueprintsโ€‹

Environment variables are excluded from Blueprints by design:

  • Export does not include environment variable values
  • Import never overwrites environment variables in the target organization

This is the key difference from Custom Variables, which transfer with Blueprints. After a Blueprint sync, custom variables carry sandbox values into production. Environment variables avoid this -- each organization keeps its own values.

Example workflow:

  1. In your sandbox, create variables like erp_api.base_url = https://sandbox.erp.example.com
  2. Configure webhooks using {{ env.erp_api.base_url }}
  3. Export the configuration as a Blueprint and import it into production
  4. In production, create the same variable key: erp_api.base_url = https://erp.example.com
  5. The same webhook config now works in both environments -- no manual changes needed

Managing variables in the UIโ€‹

Navigate to Configuration > Environments to manage your variables.

From this page you can:

  • Create new variables with a key, type, value, and optional description
  • Edit existing variable values and descriptions
  • Delete variables you no longer need
  • Search and filter by key name
  • Copy the {{ env.key }} syntax to paste into webhooks or templates

Variables are grouped by their namespace prefix (the part before the first dot) and displayed in collapsible sections.

caution

Deleting or renaming a variable breaks any configuration that references it. Check your webhooks, templates, and automations before removing a variable.

API referenceโ€‹

Base URL: https://environments.sls.epilot.io

OperationMethodPathDescription
List variablesGET/v1/environmentsReturns all variables (metadata only, secret values omitted)
Create variablePOST/v1/environmentsCreate a new variable. Returns 409 if the key already exists
Get variableGET/v1/environments/{key}Get a variable by key. Value included only for String type
Update variablePUT/v1/environments/{key}Update a variable value. Creates the variable if it doesn't exist
Delete variableDELETE/v1/environments/{key}Delete a variable by key

Authenticationโ€‹

All requests require a Bearer token in the Authorization header:

Authorization: Bearer <your-epilot-access-token>

Permissionsโ€‹

PermissionRequired for
environments:editList, create, get, and update variables
environments:deleteDelete variables

Examplesโ€‹

Create a String variableโ€‹

curl -X POST https://environments.sls.epilot.io/v1/environments \
-H "Authorization: Bearer $EPILOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "erp_api.base_url",
"type": "String",
"value": "https://erp.example.com/api/v1",
"description": "ERP API base URL"
}'

Response:

{
"key": "erp_api.base_url",
"type": "String",
"value": "https://erp.example.com/api/v1",
"description": "ERP API base URL",
"created_at": "2026-03-01T12:00:00Z",
"updated_at": "2026-03-01T12:00:00Z"
}

Create a SecretString variableโ€‹

curl -X POST https://environments.sls.epilot.io/v1/environments \
-H "Authorization: Bearer $EPILOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "erp_api.oauth_secret",
"type": "SecretString",
"value": "sk_live_abc123...",
"description": "ERP OAuth client secret"
}'

Response (note: value is omitted for secrets):

{
"key": "erp_api.oauth_secret",
"type": "SecretString",
"description": "ERP OAuth client secret",
"created_at": "2026-03-01T12:00:00Z",
"updated_at": "2026-03-01T12:00:00Z"
}

Use variables in a webhookโ€‹

Configure a webhook that uses environment variables for the URL and authentication:

Webhook configuration
{
"url": "{{ env.erp_api.base_url }}/webhooks/orders",
"authentication": {
"type": "oauth2",
"token_url": "{{ env.erp_api.oauth_token_url }}",
"client_id": "{{ env.erp_api.oauth_app_id }}",
"client_secret": "{{ env.erp_api.oauth_secret }}"
},
"headers": {
"X-Custom-Header": "{{ env.erp_api.tenant_id }}"
}
}

When the webhook fires, all {{ env.* }} references are replaced with the organization's actual values before the HTTP request is sent.

Use variables in an email templateโ€‹

Reference environment variables in email and document templates for environment-specific content:

Email template
<p>
View your order in the
<a href="https://{{ env.portal.domain }}/orders">customer portal</a>.
</p>

In sandbox, portal.domain might resolve to sandbox.mycompany.epilot.cloud. In production, it resolves to portal.mycompany.com.

Update a variable valueโ€‹

Rotating a credential is a single API call. Every webhook and integration that references the variable picks up the new value immediately:

curl -X PUT https://environments.sls.epilot.io/v1/environments/erp_api.oauth_secret \
-H "Authorization: Bearer $EPILOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"value": "sk_live_new_rotated_key..."
}'

Use the SDKโ€‹

import { epilot } from '@epilot/sdk';

epilot.authorize(accessToken);

// List all variables
const { data } = await epilot.environments.listEnvironmentVariables();
console.log(data.items);

// Create a variable
await epilot.environments.createEnvironmentVariable(null, {
key: 'my_app.api_url',
type: 'String',
value: 'https://api.example.com',
description: 'My application API URL',
});

You can also use the tree-shakeable import:

import { environments, authorize } from '@epilot/sdk/environments';

authorize(accessToken);

const { data } = await environments.listEnvironmentVariables();

Securityโ€‹

  • Encryption at rest -- All values (including String type) are encrypted using per-organization AWS KMS keys with envelope encryption.
  • Write-only secrets -- SecretString values cannot be read back through the API or UI after creation. To change a secret, you overwrite it with a new value.
  • Backend-only resolution -- Secrets are resolved only in trusted server-side contexts. They are never sent to the browser.
  • No template preview -- Secrets are excluded from template variable previews and the Template Variables API.
  • Log redaction -- Secret values are automatically redacted from all application logs.

Common patternsโ€‹

Credential rotationโ€‹

  1. Update the secret value via the API or UI
  2. All consumers pick up the new value immediately (within ~60 seconds due to caching)
  3. No need to edit individual webhooks or integrations

Multi-environment setup with Blueprintsโ€‹

  1. Design your integrations in sandbox using {{ env.* }} references
  2. Export as a Blueprint
  3. Import the Blueprint into each target organization
  4. In each organization, create the environment variables with org-specific values
  5. The same configuration works everywhere

Organizing variables by integrationโ€‹

Use dot-separated prefixes to group related variables:

# ERP integration
erp.base_url
erp.oauth_token_url
erp.oauth_client_id
erp.oauth_client_secret

# Customer portal
portal.domain
portal.support_email

# Metering
metering.webhook_url
metering.api_key