Overview
Evolution API manages WhatsApp connections through instances. Each instance represents a separate WhatsApp connection with its own authentication, session, and message queue.
Connection behavior differs between providers: Baileys requires active connection management, while Business API connections are always active.
Connection Lifecycle
Create Instance
Initialize a new WhatsApp instance: {
"instanceName" : "customer-support" ,
"token" : "your-api-key" ,
"qrcode" : true
}
Response: {
"instance" : {
"instanceName" : "customer-support" ,
"status" : "created"
},
"hash" : {
"apikey" : "generated-instance-token"
}
}
Connect to WhatsApp
For Baileys, authenticate via QR code or pairing code. For Business API, the connection is immediate. Baileys Connection: public async connectToWhatsapp ( number ?: string ): Promise < WASocket > {
try {
this . loadChatwoot ();
this . loadSettings ();
this . loadWebhook ();
this . loadProxy ();
return await this . createClient ( number );
} catch (error) {
this.logger. error ( error );
throw new InternalServerErrorException ( error ? .toString());
}
}
Business API Connection: public stateConnection : wa . StateConnection = { state: 'open' };
// Business API is always connected once configured
Monitor Status
Track connection state through webhooks or API queries: GET /instance/connectionState/:instanceName
{
"instance" : {
"instanceName" : "customer-support" ,
"state" : "open"
},
"statusReason" : 200
}
Disconnect or Delete
Gracefully disconnect or remove the instance: DELETE /instance/logout/:instanceName
# or
DELETE /instance/delete/:instanceName
Connection States
Baileys Connection States
public stateConnection : wa . StateConnection = { state: 'close' };
export interface StateConnection {
state : 'connecting' | 'open' | 'close' ;
statusReason ?: number ;
}
Instance is establishing connection to WhatsApp servers. {
"event" : "connection.update" ,
"instance" : "customer-support" ,
"state" : "connecting"
}
What’s happening:
WebSocket connection opening
Authenticating session credentials
Syncing initial data
Typical duration: 5-30 secondsInstance is connected and ready to send/receive messages. {
"event" : "connection.update" ,
"instance" : "customer-support" ,
"state" : "open" ,
"statusReason" : 200 ,
"wuid" : "[email protected] " ,
"profileName" : "Customer Support" ,
"profilePictureUrl" : "https://..."
}
Instance capabilities:
Send messages
Receive messages
Update profile
Manage groups
Access contacts
Database record updated: await this . prismaRepository . instance . update ({
where: { id: this . instanceId },
data: {
ownerJid: this . instance . wuid ,
profileName: await this . getProfileName (),
profilePicUrl: this . instance . profilePictureUrl ,
connectionStatus: 'open'
}
});
Instance is disconnected from WhatsApp. {
"event" : "connection.update" ,
"instance" : "customer-support" ,
"state" : "close" ,
"statusReason" : 428
}
Disconnect reasons (statusReason): Code Reason Auto-reconnect 200 Normal close No 401 Logged out No 403 Forbidden/banned No 402 Payment required No 406 Invalid session No 408 Connection timeout Yes 428 Connection lost Yes 500 Server error Yes
Reconnection logic: const codesToNotReconnect = [
DisconnectReason . loggedOut ,
DisconnectReason . forbidden ,
402 ,
406
];
const shouldReconnect = ! codesToNotReconnect . includes ( statusCode );
if ( shouldReconnect ) {
await this . connectToWhatsapp ( this . phoneNumber );
}
Reconnection Strategies
Automatic Reconnection
Evolution API handles reconnections automatically for transient failures:
if ( connection === 'close' ) {
const statusCode = ( lastDisconnect ?. error as Boom )?. output ?. statusCode ;
const shouldReconnect = ! codesToNotReconnect . includes ( statusCode );
if ( shouldReconnect ) {
await this . connectToWhatsapp ( this . phoneNumber );
} else {
// Permanent disconnection - log out
this . eventEmitter . emit ( 'logout.instance' , this . instance . name , 'inner' );
this . client ?. ws ?. close ();
this . client . end ( new Error ( 'Close connection' ));
}
}
Automatic reconnection includes exponential backoff to avoid overwhelming WhatsApp servers.
Manual Reconnection
Force reconnection of an existing instance:
POST /instance/connect/:instanceName
{
"number" : "5511999999999"
}
Backend implementation:
public async reloadConnection (): Promise < WASocket > {
try {
return await this . createClient ( this . phoneNumber );
} catch (error) {
this.logger. error ( error );
throw new InternalServerErrorException ( error ? .toString());
}
}
Session Persistence
Evolution API persists session data to enable seamless reconnections:
Storage Options
Database (Prisma)
Redis Cache
File Provider
Store encrypted credentials in PostgreSQL or MySQL: DATABASE_SAVE_DATA_INSTANCE = true
DATABASE_PROVIDER = postgresql
DATABASE_CONNECTION_URI = 'postgresql://user:pass@host:5432/evolution'
Implementation: if ( db . SAVE_DATA . INSTANCE ) {
return await useMultiFileAuthStatePrisma ( this . instance . id , this . cache );
}
Pros:
Persistent across restarts
Centralized management
Easy backup
Cons:
Database dependency
Slightly slower than Redis
Store sessions in Redis for faster access: CACHE_REDIS_ENABLED = true
CACHE_REDIS_URI = redis://localhost:6379/6
CACHE_REDIS_SAVE_INSTANCES = true
Implementation: if ( cache ?. REDIS . ENABLED && cache ?. REDIS . SAVE_INSTANCES ) {
return await useMultiFileAuthStateRedisDb ( this . instance . id , this . cache );
}
Pros:
Fastest reconnection
Reduces database load
Automatic expiration
Cons:
Volatile (data loss on Redis restart)
Requires Redis infrastructure
Store sessions in custom storage (S3, MinIO, etc.): PROVIDER_ENABLED = true
S3_ENABLED = true
S3_BUCKET = evolution-sessions
Implementation: if ( provider ?. ENABLED ) {
return await this . authStateProvider . authStateProvider ( this . instance . id );
}
Pros:
Scalable storage
Independent infrastructure
Cloud-native
Cons:
Network latency
Additional configuration
Connection Monitoring
Webhook Events
Subscribe to connection events via webhooks:
WEBHOOK_GLOBAL_ENABLED = true
WEBHOOK_GLOBAL_URL = https://your-server.com/webhook
WEBHOOK_EVENTS_CONNECTION_UPDATE = true
WEBHOOK_EVENTS_QRCODE_UPDATED = true
Event payload:
{
"event" : "connection.update" ,
"instance" : "customer-support" ,
"data" : {
"instance" : "customer-support" ,
"state" : "open" ,
"statusReason" : 200 ,
"wuid" : "[email protected] " ,
"profileName" : "Customer Support" ,
"profilePictureUrl" : "https://pps.whatsapp.net/..."
},
"date_time" : "2024-03-04T10:30:00.000Z" ,
"sender" : "evolution-api" ,
"server_url" : "https://evolution.example.com" ,
"apikey" : "instance-api-key"
}
API Polling
Query connection status programmatically:
GET /instance/connectionState/:instanceName
Response:
{
"instance" : {
"instanceName" : "customer-support" ,
"state" : "open"
},
"statusReason" : 200
}
Health Checks
Implement health monitoring:
public get connectionStatus () {
return this . stateConnection ;
}
// Usage in monitoring
const status = instance . connectionStatus ;
if ( status . state !== 'open' ) {
// Alert or reconnect
}
Multi-Instance Management
Manage multiple WhatsApp connections:
# List all instances
GET /instance/fetchInstances
[
{
"instanceName" : "customer-support" ,
"connectionStatus" : "open" ,
"ownerJid" : "[email protected] "
},
{
"instanceName" : "sales-team" ,
"connectionStatus" : "connecting" ,
"ownerJid" : null
}
]
Instance Isolation
Evolution API is multi-tenant. Always scope operations by instance:
// CORRECT - Instance-scoped query
const messages = await prismaRepository . message . findMany ({
where: { instanceId: instance . id }
});
// INCORRECT - Cross-instance leak
const messages = await prismaRepository . message . findMany ();
Connection Best Practices
Monitor Connection Events
Subscribe to connection.update webhooks to track instance health: app . post ( '/webhook' , ( req , res ) => {
const { event , data } = req . body ;
if ( event === 'connection.update' ) {
if ( data . state === 'close' ) {
// Handle disconnection
console . log ( `Instance ${ data . instance } disconnected:` , data . statusReason );
}
}
res . sendStatus ( 200 );
});
Enable Session Persistence
Always enable session storage to prevent re-authentication: DATABASE_SAVE_DATA_INSTANCE = true
# or
CACHE_REDIS_SAVE_INSTANCES = true
Handle QR Code Expiration
Baileys QR codes expire after a limit: QRCODE_LIMIT = 30 # Maximum regenerations
Monitor qrcode.updated events and alert users to scan promptly.
Implement Graceful Shutdown
Properly close connections on application shutdown: process . on ( 'SIGTERM' , async () => {
await instance . client ?. logout ( 'Shutting down' );
await instance . client ?. ws ?. close ();
process . exit ( 0 );
});
Connection Limits
Baileys Limitations
Concurrent connections per number : 1 active connection
QR code timeout : ~45 seconds per code
Max QR regenerations : Configurable (default 30)
Reconnection delay : Automatic exponential backoff
Multiple simultaneous connections with the same WhatsApp number will cause disconnections.
Business API Limitations
No active connection required : Always available
Rate limits : Based on account tier
Webhook timeout : 5-second response required
Troubleshooting
Connection Keeps Closing
Check logs for disconnect reason:
GET /instance/connectionState/:instanceName
Common causes:
StatusReason Cause Solution 401 Logged out manually Re-authenticate with QR code 403 Number banned Use different number 428 Connection timeout Check network/firewall 500 WhatsApp server error Wait and retry
Session Not Persisting
Verify storage configuration:
// Check logs for auth state provider
this . logger . info ( 'Using auth state provider: Prisma/Redis/Provider' );
Ensure database or Redis is accessible:
# Test database connection
POST /instance/create
# Check logs for connection errors
Instance Not Found
Query instance existence:
GET /instance/fetchInstances?instanceName=customer-support
If missing, recreate:
Next Steps
Baileys Connection Learn Baileys-specific connection details
Business API Understand Business API connection flow
Webhooks Configure connection event webhooks
Instance Management Create and manage instances