open-wa v5 is alpha. Use v4.76.0 for mature production systems unless you are validating v5.
The Client APIAPI ExplorerLicensing

Managing Secrets in Config

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

Secrets Wally

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-abc123

Load 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-automate

Or 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 .env files to git
  • Use secret management services for production (Doppler, Vault, etc.)
  • Rotate secrets regularly

Secret Rotation

To rotate a secret:

  1. Update the environment variable or config file
  2. Restart the session
  3. 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');
  }
}
Wally the Walrus typing

Was this helpful?

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

On this page