Configure Webflow across development, staging, and production environments with per-environment API tokens, site IDs, and secret management via Vault/AWS/GCP. Trigger with phrases like "webflow environments", "webflow staging", "webflow dev prod", "webflow environment setup", "webflow config by env".
Configure Webflow Data API v2 integrations across development, staging, and production with separate API tokens, site IDs, and secret management. Each environment targets a different Webflow site for complete isolation.
| Environment | Webflow Site | API Token | CMS Data | Purpose |
|---|---|---|---|---|
| Development | Dev site | Dev token | Test/seed data | Local development |
| Staging |
| Staging site |
| Staging token |
| Copy of prod data |
| Pre-prod validation |
| Production | Production site | Prod token | Real data | Live traffic |
// src/config/webflow.ts
type Environment = "development" | "staging" | "production";
interface WebflowEnvConfig {
accessToken: string;
siteId: string;
maxRetries: number;
webhookSecret: string;
debug: boolean;
}
function detectEnvironment(): Environment {
const env = process.env.NODE_ENV || "development";
const valid: Environment[] = ["development", "staging", "production"];
return valid.includes(env as Environment) ? (env as Environment) : "development";
}
export function getWebflowConfig(): WebflowEnvConfig {
const env = detectEnvironment();
// Each environment has its own token and site
const configs: Record<Environment, () => WebflowEnvConfig> = {
development: () => ({
accessToken: requireEnv("WEBFLOW_API_TOKEN"),
siteId: requireEnv("WEBFLOW_SITE_ID"),
maxRetries: 1,
webhookSecret: process.env.WEBFLOW_WEBHOOK_SECRET || "",
debug: true,
}),
staging: () => ({
accessToken: requireEnv("WEBFLOW_API_TOKEN_STAGING"),
siteId: requireEnv("WEBFLOW_SITE_ID_STAGING"),
maxRetries: 2,
webhookSecret: requireEnv("WEBFLOW_WEBHOOK_SECRET_STAGING"),
debug: false,
}),
production: () => ({
accessToken: requireEnv("WEBFLOW_API_TOKEN_PROD"),
siteId: requireEnv("WEBFLOW_SITE_ID_PROD"),
maxRetries: 3,
webhookSecret: requireEnv("WEBFLOW_WEBHOOK_SECRET_PROD"),
debug: false,
}),
};
const config = configs[env]();
console.log(`[webflow] Environment: ${env}, Site: ${config.siteId.substring(0, 8)}...`);
return config;
}
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Missing required env var: ${name}`);
return value;
}
# .env.development (local, git-ignored)
WEBFLOW_API_TOKEN=dev-token-here
WEBFLOW_SITE_ID=dev-site-id-here
WEBFLOW_WEBHOOK_SECRET=dev-webhook-secret
# .env.staging (stored in CI/deployment platform, never committed)
WEBFLOW_API_TOKEN_STAGING=staging-token-here
WEBFLOW_SITE_ID_STAGING=staging-site-id-here
WEBFLOW_WEBHOOK_SECRET_STAGING=staging-webhook-secret
# .env.production (stored in vault/secret manager, never committed)
WEBFLOW_API_TOKEN_PROD=prod-token-here
WEBFLOW_SITE_ID_PROD=prod-site-id-here
WEBFLOW_WEBHOOK_SECRET_PROD=prod-webhook-secret
# .env.example (committed to repo, no real values)
WEBFLOW_API_TOKEN=your-token-here
WEBFLOW_SITE_ID=your-site-id-here
WEBFLOW_WEBHOOK_SECRET=your-webhook-secret
# Store secrets
aws secretsmanager create-secret \
--name webflow/production \
--secret-string '{
"WEBFLOW_API_TOKEN": "prod-token",
"WEBFLOW_SITE_ID": "prod-site-id",
"WEBFLOW_WEBHOOK_SECRET": "prod-webhook-secret"
}'
# Retrieve in application
aws secretsmanager get-secret-value \
--secret-id webflow/production \
--query SecretString --output text | jq .
// Load from AWS at startup
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
async function loadWebflowSecrets(env: string) {
const client = new SecretsManagerClient({});
const command = new GetSecretValueCommand({
SecretId: `webflow/${env}`,
});
const response = await client.send(command);
const secrets = JSON.parse(response.SecretString!);
process.env.WEBFLOW_API_TOKEN_PROD = secrets.WEBFLOW_API_TOKEN;
process.env.WEBFLOW_SITE_ID_PROD = secrets.WEBFLOW_SITE_ID;
}
# Store secrets
echo -n "prod-token" | gcloud secrets create webflow-api-token-prod --data-file=-
echo -n "prod-site-id" | gcloud secrets create webflow-site-id-prod --data-file=-
# Use in Cloud Run
gcloud run deploy webflow-service \
--set-secrets="WEBFLOW_API_TOKEN_PROD=webflow-api-token-prod:latest,WEBFLOW_SITE_ID_PROD=webflow-site-id-prod:latest"
# Store secrets
vault kv put secret/webflow/production \
api_token="prod-token" \
site_id="prod-site-id" \
webhook_secret="prod-webhook-secret"
# Retrieve
vault kv get -field=api_token secret/webflow/production
Prevent destructive operations in wrong environment:
function requireProduction(operation: string): void {
const config = getWebflowConfig();
if (config.debug) {
throw new Error(
`${operation} is production-only. Current: ${detectEnvironment()}`
);
}
}
function blockProduction(operation: string): void {
const env = detectEnvironment();
if (env === "production") {
throw new Error(
`${operation} is blocked in production. Use staging for testing.`
);
}
}
// Usage
async function publishSite(siteId: string) {
requireProduction("publishSite"); // Only in production
await webflow.sites.publish(siteId, { publishToWebflowSubdomain: true });
}
async function deleteAllItems(collectionId: string) {
blockProduction("deleteAllItems"); // Never in production
const { items } = await webflow.collections.items.listItems(collectionId);
const ids = items!.map(i => i.id!);
await webflow.collections.items.deleteItemsBulk(collectionId, { itemIds: ids });
}
# .github/workflows/webflow-deploy.yml