Skip to main content

Authentication Overview

Evolution API uses API key authentication to secure all endpoints. There are two types of authentication:
  1. Global API Key - Full access to all instances and administrative operations
  2. Instance Token - Restricted access to a specific instance only
All API requests require an apikey header with either a global key or instance-specific token.
API keys are passed via the apikey HTTP header (not Authorization). This allows you to use standard authentication schemes like JWT for your own application layer.

Global API Key

The global API key grants full administrative access to your Evolution API installation.

Setting the Global Key

Configure in your .env file:
# Define a global apikey to access all instances.
AUTHENTICATION_API_KEY=429683C4C977415CAAFCCE10F7D57E11
Generate a strong, random API key for production. Use at least 32 characters with mixed alphanumeric characters. Never use the example key above.

Generate a Secure Key

# Generate a secure random key
openssl rand -hex 32

Global Key Capabilities

With the global API key, you can:
  • Create new instances
  • Delete any instance
  • Fetch information about all instances
  • Access any instance’s endpoints
  • Modify system-wide configurations
# Create instance with global key
curl -X POST https://your-api.com/instance/create \
  -H "apikey: 429683C4C977415CAAFCCE10F7D57E11" \
  -H "Content-Type: application/json" \
  -d '{"instanceName": "new-instance"}'

# Fetch all instances
curl -X GET https://your-api.com/instance/fetchInstances \
  -H "apikey: 429683C4C977415CAAFCCE10F7D57E11"

Instance Tokens

Each instance has its own unique authentication token, providing scoped access to that instance only.

How Instance Tokens Work

When you create an instance, Evolution API generates a unique token:
{
  "instance": {
    "instanceName": "my-whatsapp",
    "instanceId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "hash": "B5F6E890D1234567890ABCDEF1234567"  // This is the instance token
}

Custom Instance Tokens

You can specify a custom token during instance creation:
{
  "instanceName": "my-whatsapp",
  "token": "my-custom-secure-token-here",
  "qrcode": true
}
Custom tokens must be unique across all instances. The API will reject duplicate tokens.
From the source code (src/api/services/auth.service.ts:7):
public async checkDuplicateToken(token: string) {
  if (!token) {
    return true;
  }

  const instances = await this.prismaRepository.instance.findMany({
    where: { token },
  });

  if (instances.length > 0) {
    throw new BadRequestException('Token already exists');
  }

  return true;
}

Using Instance Tokens

Instance tokens restrict API access to only that instance:
# Send message with instance token
curl -X POST https://your-api.com/message/sendText/my-whatsapp \
  -H "apikey: B5F6E890D1234567890ABCDEF1234567" \
  -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "text": "Hello from Evolution API!"
  }'

# Check connection state
curl -X GET https://your-api.com/instance/connectionState/my-whatsapp \
  -H "apikey: B5F6E890D1234567890ABCDEF1234567"
Attempting to access a different instance with this token will fail:
# This will return 401 Unauthorized
curl -X GET https://your-api.com/instance/connectionState/different-instance \
  -H "apikey: B5F6E890D1234567890ABCDEF1234567"

Authentication Flow

Here’s how Evolution API validates requests:
1

Extract API Key

The API extracts the apikey header from your request:
// From src/api/guards/auth.guard.ts:12
const key = req.get('apikey');

if (!key) {
  throw new UnauthorizedException();
}
2

Check Global Key

First, check if the key matches the global API key:
// From src/api/guards/auth.guard.ts:19
if (env.KEY === key) {
  return next(); // Global access granted
}
3

Check Instance Token

If not a global key, verify it’s a valid instance token:
// From src/api/guards/auth.guard.ts:29
const instance = await prismaRepository.instance.findUnique({
  where: { name: param.instanceName },
});

if (instance.token === key) {
  return next(); // Instance access granted
}
4

Reject Invalid Keys

If neither match, reject the request:
throw new UnauthorizedException();

Security Best Practices

Always use HTTPS to encrypt API keys in transit:
# ✅ Good - Encrypted connection
https://your-api.com/instance/create

# ❌ Bad - Exposes API key in plain text
http://your-api.com/instance/create
Configure SSL in your .env:
SERVER_TYPE=https
SSL_CONF_PRIVKEY=/path/to/private.key
SSL_CONF_FULLCHAIN=/path/to/fullchain.crt
Implement a key rotation policy:
  1. Generate a new global API key
  2. Update your .env file
  3. Restart Evolution API
  4. Update all client applications
For instance tokens, delete and recreate instances with new tokens:
# Delete old instance
curl -X DELETE https://your-api.com/instance/delete/my-instance \
  -H "apikey: OLD_TOKEN"

# Create with new token
curl -X POST https://your-api.com/instance/create \
  -H "apikey: GLOBAL_KEY" \
  -d '{"instanceName": "my-instance", "token": "NEW_TOKEN"}'
Never hardcode API keys in your application:
// ❌ Bad - Hardcoded key
const apiKey = 'B5F6E890D1234567890ABCDEF1234567';

// ✅ Good - Environment variable
const apiKey = process.env.EVOLUTION_API_KEY;

// ✅ Good - Secrets manager
const apiKey = await secretsManager.getSecret('evolution-api-key');
Use environment variables, secret managers, or encrypted configuration files.
For customer-facing applications, always use instance tokens instead of the global key:
// Multi-tenant SaaS example
class WhatsAppService {
  async sendMessage(customerId, message) {
    // Each customer gets their own instance token
    const instanceToken = await db.getInstanceToken(customerId);
    
    return fetch(`${EVOLUTION_API}/message/sendText/${customerId}`, {
      method: 'POST',
      headers: {
        'apikey': instanceToken,  // Not the global key!
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(message)
    });
  }
}
Track failed authentication attempts:
// Monitor 401 responses
const response = await fetch(endpoint, { headers });

if (response.status === 401) {
  // Log and alert on authentication failures
  logger.error('Authentication failed', { 
    endpoint, 
    timestamp: new Date() 
  });
  
  // Implement rate limiting
  await rateLimit.increment(ip);
}
Restrict API access by IP address at the network level:
# Nginx configuration
location /instance/create {
  allow 203.0.113.0/24;  # Your office network
  deny all;
  proxy_pass http://evolution-api:8080;
}
Or use a firewall/VPC to limit access to your Evolution API server.

Authentication in Different Languages

const EVOLUTION_API = 'https://your-api.com';
const GLOBAL_API_KEY = process.env.EVOLUTION_GLOBAL_KEY;
const INSTANCE_TOKEN = process.env.EVOLUTION_INSTANCE_TOKEN;

// Using global key to create instance
async function createInstance(name) {
  const response = await fetch(`${EVOLUTION_API}/instance/create`, {
    method: 'POST',
    headers: {
      'apikey': GLOBAL_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ instanceName: name })
  });
  
  if (!response.ok) {
    throw new Error(`Authentication failed: ${response.status}`);
  }
  
  return response.json();
}

// Using instance token to send message
async function sendMessage(instanceName, number, text) {
  const response = await fetch(
    `${EVOLUTION_API}/message/sendText/${instanceName}`,
    {
      method: 'POST',
      headers: {
        'apikey': INSTANCE_TOKEN,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ number, text })
    }
  );
  
  return response.json();
}

Expose Instances Configuration

Control which instances are visible in fetch requests:
# In .env
AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true
  • true - All instances are returned in fetch requests
  • false - Instances are hidden, only explicitly requested instances are returned
This adds privacy for multi-tenant deployments where customers shouldn’t see other instances.

Common Authentication Errors

{
  "statusCode": 401,
  "message": "Unauthorized",
  "error": "Unauthorized"
}
Cause: No apikey header providedSolution: Add the header to your request:
curl -H "apikey: YOUR_API_KEY" ...
Cause: Wrong API key or tokenSolution: Verify you’re using the correct key:
  • Check .env file for global key
  • Confirm instance token from creation response
  • Ensure no extra spaces or newlines in the key
{
  "statusCode": 403,
  "message": "Forbidden",
  "error": "Forbidden"
}
Cause: Using instance token for a different instanceSolution: Use the correct token for each instance or use the global API key
{
  "statusCode": 400,
  "message": "Token already exists"
}
Cause: Trying to create instance with a token that’s already in useSolution: Choose a unique token or let Evolution API generate one automatically

Next Steps

Instances

Learn how to create and manage instances

Webhooks

Configure webhooks for real-time event notifications

Multi-Tenant

Build secure multi-tenant applications

API Reference

Explore all available API endpoints

Build docs developers (and LLMs) love