evolution-api-management
Skill1 starsChanged 8 months agoApache-2.0View on GitHub
Use when deploying, connecting, troubleshooting, or managing Evolution API WhatsApp instances on ECS Fargate. Triggers on QR code generation, WhatsApp session conflicts, webhook JWT errors, instance lifecycle operations, or environment isolation for WhatsApp services.
- Reads credentials
In this repository11 other files it gives its agents.
- ai-tone-calibrationSkill
- estimating-agent-tasksSkill
- captureSkill
- connectSkill
- emergeSkill
- reviewSkill
- obsidian-second-brainSkill
- todaySkill
- quality-gateSkill
- validate-claude-code-setupSkill
- video-to-contextSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 26
All endpoints require header: apikey: $AUTHENTICATION_API_KEY
- Very specific: line 50
Use grep or tail to extract useful data
- Very specific: line 99
Call /instance/connect/{name} - returns { code, base64, pairingCode }
- Distinctive: line 102
Create HTML with embedded base64 image (no CDN dependency)
- Very specific: line 133
When WHATSAPP_MOCK=true, getEvolutionStatus() must return early with a healthy stub.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Evolution API Management
Overview
Operational guide for Evolution API v2.3.7 on ECS Fargate with WhatsApp Baileys integration. Covers instance lifecycle, session management, environment isolation, and troubleshooting.
Core constraint: WhatsApp allows only ONE active Baileys connection per phone number. Two instances with the same number = continuous conflict/replaced loop every ~2 seconds.
Quick Reference
| Operation | Endpoint | Method |
| List instances | /instance/fetchInstances | GET |
| Create instance | /instance/create | POST |
| Connect (QR) | /instance/connect/{name} | GET |
| Connection state | /instance/connectionState/{name} | GET |
| Logout | /instance/logout/{name} | DELETE |
| Delete instance | /instance/delete/{name} | DELETE |
| Set webhook | /webhook/set/{name} | POST |
All endpoints require header: apikey: $AUTHENTICATION_API_KEY
ECS Exec Commands
Container is BusyBox-based: no curl/wget. Use node -e for everything.
# Template for all ECS exec commands
aws ecs execute-command \
--cluster "$CLUSTER" \
--task "$TASK_ID" \
--container EvolutionService \
--interactive \
--command 'node -e "...script..."'Get task ID:
TASK_ID=$(aws ecs list-tasks --cluster "$CLUSTER" --service-name EvolutionService \
--query 'taskArns[0]' --output text | awk -F/ '{print $NF}')ECS exec output gotchas:
- Output always contains
Starting session...andCannot perform start session: EOFnoise - Use
greportailto extract useful data - Long outputs get truncated (~3KB). For large data: write to file inside container, read in chunks via base64
- Shell escaping through ECS exec is fragile. For complex payloads: base64-encode the JSON body, decode inside the node script
Instance Lifecycle
Create Instance with Webhook
// Body for POST /instance/create
{
instanceName: 'alinhou-production',
integration: 'WHATSAPP-BAILEYS',
qrcode: true,
rejectCall: false,
groupsIgnore: true,
alwaysOnline: false,
readMessages: false,
readStatus: false,
syncFullHistory: false,
webhook: {
url: 'https://lambda-url/api/v1/webhook/evolution',
byEvents: true,
base64: false,
events: ['MESSAGES_UPSERT'],
headers: { jwt_key: 'YOUR_WEBHOOK_SECRET' } // MUST have a value
}
}Update Webhook on Existing Instance
// POST /webhook/set/{instanceName}
// MUST wrap in "webhook" property
{
webhook: {
enabled: true,
url: 'https://lambda-url/api/v1/webhook/evolution',
webhookByEvents: true,
webhookBase64: false,
events: ['MESSAGES_UPSERT'],
headers: { jwt_key: 'YOUR_WEBHOOK_SECRET' }
}
}QR Code Generation Workflow
- Call
/instance/connect/{name}- returns{ code, base64, pairingCode } - The
codefield is the raw QR data string (~240 chars) - Generate QR locally:
npx qrcode -o /tmp/qr.png "$CODE"or userequire('qrcode').toDataURL() - Create HTML with embedded base64 image (no CDN dependency)
- Copy to Windows:
/mnt/c/Users/{user}/Downloads/ - QR expires in ~45 seconds - be fast
Never try to pipe base64 image data through ECS exec - it gets truncated and corrupted.
Environment Isolation
The Problem
WhatsApp Baileys protocol enforces single connection per phone number. Staging + production competing = session conflict loop.
The Solution
| Component | Staging | Production |
WHATSAPP_MOCK | 'true' | 'false' |
EVOLUTION_INSTANCE_NAME | alinhou-staging | alinhou-production |
| ECS scaling | { min: 0, max: 0 } | { min: 1, max: 1 } |
| Evolution DB | /evolution | /evolution_prod |
| Outage checker | Returns mock stub | Real Evolution calls |
In SST infra:
WHATSAPP_MOCK: $app.stage === 'production' ? 'false' : 'true',
EVOLUTION_INSTANCE_NAME: `alinhou-${$app.stage}`,
scaling: $app.stage === 'production' ? { min: 1, max: 1 } : { min: 0, max: 0 },Outage Checker Mock Guard
When WHATSAPP_MOCK=true, getEvolutionStatus() must return early with a healthy stub. Otherwise, it tries to reach a non-existent Evolution container and triggers false-positive outage alert emails after 30 minutes.
Webhook JWT Configuration
jwt_keygoes insideheadersobject in the webhook config- Evolution strips
jwt_key, generates HS256 JWT with{ app: "evolution", action: "webhook" } - JWT has 10-minute expiry, sent as
Authorization: Bearer <token> - Lambda verifies using
EVOLUTION_WEBHOOK_SECRETenv var EVOLUTION_WEBHOOK_SECRETis NOT in the Evolution container env - only in Lambda. When creating instances via ECS exec, you must pass the secret value directly (get fromnpx sst secret list)- Missing JWT key causes:
secretOrPrivateKey must have a valueerror in Evolution logs
Session Persistence
Dual strategy for maximum resilience:
- Database: PostgreSQL
Sessiontable (whenDATABASE_SAVE_DATA_INSTANCE=true) - EFS: Mounted at
/evolution/instances/for local Baileys auth files
Either alone is sufficient. Both together = defense in depth.
Deploy Strategy (Stop-First)
// In infra/evolution.ts transform
deploymentMaximumPercent: 100,
deploymentMinimumHealthyPercent: 0,Old task stops before new starts. ~3-5 min downtime acceptable:
- Outbound messages queue in SQS
- Inbound messages buffered by WhatsApp servers
Brazilian Phone Normalization
WhatsApp sends 12-digit format: 558188101603 (drops mobile 9th digit)
Database stores 13-digit format: 5581988101603 (with the 9)
Webhook normalizes: if 12 digits starting with 55, insert 9 at position 4.
Troubleshooting
| Symptom | Cause | Fix |
conflict/replaced loop every ~2s | Two instances sharing same phone | Scale one environment to 0 |
secretOrPrivateKey must have a value | JWT key empty in webhook config | Update webhook with POST /webhook/set/{name} and actual secret value |
Instance stuck in connecting | Session conflict or stale auth | Logout, wait 5s, reconnect with fresh QR |
| 403 on instance create | Instance name already exists | Delete old instance first |
| QR scan says "cannot connect device" | Session conflict with another environment | Ensure only one environment has Evolution running |
| Messages not delivered | Phone number format mismatch | Check 12→13 digit normalization in webhook |
| Zombie instance (DB=open, WS=dead) | Container restart without clean disconnect | Delete from DB, clear EFS, force new deployment, recreate instance |
Container Details
- Image:
evoapicloud/evolution-api:v2.3.7(don't upgrade without staging test) - BusyBox-based: no curl, no wget, use
node -e startPeriod: 180s(container + EFS mount + Prisma + Baileys reconnect)- Health check: node HTTP GET to localhost:8080
LOG_LEVEL: 'ERROR,WARN,WEBHOOKS'for clean CloudWatch
Cost
Staging Evolution running 24/7 (0.25 vCPU, 1GB ARM64, sa-east-1): ~$8.51/month. Scaling to 0 with mock mode saves this entirely.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

