Managing Secrets in Config
Handle API keys, passwords, and tokens safely in wa.config.js and pluginConfig.

Managing Secrets in Config
This guide covers how to safely manage API keys, passwords, and other secrets in your open-wa configuration.
Environment Variables
Using process.env
Reference environment variables in your config file:
// wa.config.js
export default {
apiKey: process.env.OPENWA_API_KEY,
webhook: {
url: process.env.WEBHOOK_URL,
secret: process.env.WEBHOOK_SECRET,
},
pluginConfig: {
'my-plugin': {
apiKey: process.env.MY_PLUGIN_API_KEY,
},
},
};.env File
Create a .env file in your project root:
OPENWA_API_KEY=your-api-key
WEBHOOK_URL=https://your-app.example/webhooks
WEBHOOK_SECRET=your-webhook-secret
MY_PLUGIN_API_KEY=sk-abc123Load it before your config runs. Many setups automatically load .env files through Node.js or your framework.
Docker Environment Variables
Pass secrets via Docker environment variables:
docker run -p 8080:8080 \
-e OPENWA_API_KEY=your-key \
-e MY_PLUGIN_API_KEY=sk-abc123 \
openwa/wa-automateOr use a .env file with Docker Compose:
# docker-compose.yml
services:
open-wa:
image: openwa/wa-automate
env_file:
- .env
ports:
- "8080:8080"pluginConfig Structure
Key Mapping
Each plugin reads its config from pluginConfig using its meta.name:
export default {
pluginConfig: {
'webhook': { url: 'https://...' },
'chatwoot': { instanceUrl: 'https://...' },
'my-plugin': { apiKey: '...' },
},
};Nested Objects
Config can include nested objects:
export default {
pluginConfig: {
'moderation': {
apiKey: process.env.OPENAI_API_KEY,
actions: {
delete: true,
warn: true,
notify: false,
},
groups: ['group1@g.us', 'group2@g.us'],
},
},
};Boolean Toggles
Use boolean values for feature toggles:
export default {
pluginConfig: {
'voice-transcriber': {
enabled: true,
language: 'en',
},
},
};Security
Logging Risks
Never log config objects directly. Secrets will appear in logs:
// BAD - logs the API key
logger.info('Config loaded', { config });
// GOOD - log only non-sensitive fields
logger.info('Config loaded', {
hasKey: !!config.apiKey,
url: config.url,
});Safe Storage
- Use environment variables for secrets, not hardcoded values
- Never commit
.envfiles to git - Use secret management services for production (Doppler, Vault, etc.)
- Rotate secrets regularly
Secret Rotation
To rotate a secret:
- Update the environment variable or config file
- Restart the session
- The plugin will reload with the new secret
Validation
Using Zod
Validate secrets at startup:
const configSchema = z.object({
apiKey: z.string().min(1, 'API key is required'),
apiUrl: z.string().url(),
});Graceful Handling
For optional secrets, use Zod optional fields:
const configSchema = z.object({
apiKey: z.string().optional(),
apiUrl: z.string().url().default('https://api.example.com'),
});
init: async ({ config }) => {
if (!config.apiKey) {
logger.warn('No API key configured, some features will be disabled');
}
}Related
- Plugin security model - Plugin security boundaries
- Security and deployment - Production security

Was this helpful?
Wally and his cute companion coffee mug are coding day and night to keep this up-to-date!
