Waha – WhatsApp API

Saudações.

Vou apresentar o software WAHA (WhatsApp HTTP API), um software que provê a API não oficial do WhatsApp para suas integrações, automações e IA.

Ele é uma alternativa ao antigo Evolution API (agora https://evolutionfoundation.com.br/).

1 – O que é o Waha

O WAHA (site: https://waha.devlike.pro/) é um gateway (software que fica entre dois sistemas) que roda em container Docker e se conecta a uma ou várias contas do WhatsApp como se fosse o app WhatsApp Web (app do Zap que você usa pelo navegador ou pelo desktop).

De um lado ele se conecta a sua conta WhatsApp, do outro ele te fornece:

  • APIs HTTP para envio e recebimento de mensagens;
  • Interface Web para envio e recebimento de mensagens;
  • Integração com ChatWoot;

Ele era distribuído em duas versões, community e plus. Recentemente os desenvolvedores abriram a versão plus para uso gratuito e ilimitado.

2 – Cuidados

Antes de utilizar o WAHA é importante salientar que ele é um software não oficial, ou seja, a META (Face/Zap/Insta) não autoriza o uso de softwares desse tipo pois obviamente concorrem com o WhatsApp Business API (WABA).

2.1 – Riscos

Os riscos são:

  • Bloqueio temporário da sua conta (número);
  • Bloqueio permanente e irreversível da sua conta;

Esse tipo de sistema não tem futuro, a META está constantemente alterando o canal de comunicação entre os clientes (WhatsApp Desktop, WhatsApp Web, APP) para dificultar a vida dos desenvolvedores e motivar as vendas do WABA.

2.2 – Manutenção constante

Por causa desse alteração constante você deve sempre manter o WAHA atualizado semanalmente (fazer pull da imagem latest e recriar o container, os dados não são perdidos pois ficam armazenados no PostgreSQL e no S3).

2.3 – Recomendações

Recomendo utilizar para pequenas tarefas de automação e interação com sistemas e agentes de IA.

Cuidados contantes:

  • Atualizar a imagem do container semanalmente e recriando o container do Waha;
  • Usar em números “aquecidos” (usados por humanos por alguns dias antes de ligar nos bots);
  • Não enviar mensagens para contatos que não possuem seu número salvo, cada nova mensagem “selvagem” é um ponto negativo na sua conta. Cada contato selvagem que reporta sua mensagem como spam acelera o bloqueio da sua conta;
  • Não enviar mensagens com intervalos curtos, um humano não consegue enviar 10 mensagens por segundo, é uma indicação obvia de uso irregular do WhatsApp;
  • Não enviar mensagens com intervalos fixos (como 1 mensagem a cada 5 segundos), coloque sempre um fator aleatório nos intervalos dos loops em suas automações;
  • Não envie a mesma mensagem para vários contatos (mesmo HASH), sempre adicione alguma frase ou número diferente ou coloque a IA com temperature aumentada.

Uso comum e tranquilo:

  • Monitoramento de sistemas e eventos;
  • Interação com agentes de IA;
  • Comunicação com clientes e contatos conhecidos sem envio em massa.

Para todos os demais casos, contrate o WABA, nele você não poderá cometer os mesmo erros mas poderá aumentar a taxa de envio levemente (você pagará por isso!).

3 – Preparativos

O Waha precisa de:

  • Um banco de dados – PostgreSQL;
  • Um cache RESP – Redis ou Valkey;
  • Um storage S3 – Minio.

3.1 – Rede

Crie a rede Docker “network_public“:

Bash
# Rede de containers
docker network create network_public \
    -d bridge \
    -o com.docker.network.bridge.name=br-net-public \
    -o com.docker.network.driver.mtu=1500 \
    -o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
    --subnet 10.249.0.0/16 \
    --gateway 10.249.255.254;

3.2 – Banco de dados PostgreSQL

Vou usar a imagem pgvector (debian trixie + postgres 18 + pgvector).

O nome do container será “waha-database” e as credenciais:

  • Usuário: postgres
  • Senha: tulipasql
  • Banco de dados: waha

Script em shell:

Bash
# Variaveis
    NAME="waha-database";

# Argumentos do postgres (usado somente no primeiro boot)
    POSTGRES_USER="postgres";
    POSTGRES_PASSWORD="tulipasql";
    POSTGRES_DB="waha";

# Imagem
    IMAGE="pgvector/pgvector:pg18-trixie";
    docker pull $IMAGE;

# Volume
    # obs: pg18 usa pasta principal interna: /var/lib/postgresql
    DATADIR=/storage/$NAME;
    mkdir -p          $DATADIR;
    chown -R 999:999  $DATADIR;

# Renovar/rodar:
    docker rm -f $NAME 2>/dev/null;
    docker run \
        -d \
        --restart            always \
        --name               $NAME \
        --hostname           $NAME.intranet.br \
        \
        --cpus                2.0 \
        --cpu-shares         1024 \
        --memory               2g \
        --memory-swap          2g \
        --memory-reservation   1g \
        --shm-size             1g \
        \
        --network            network_public \
        --ip                 10.249.130.1 \
        \
        --tmpfs              /run:rw,noexec,nosuid,size=64m \
        --tmpfs              /tmp:rw,exec,suid,size=64m \
        \
        -v $DATADIR:/var/lib/postgresql \
        \
        -e POSTGRES_USER=$POSTGRES_USER \
        -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
        -e POSTGRES_DB=$POSTGRES_DB \
        -e PGDATA=/var/lib/postgresql/data/pgdata \
        \
        --health-cmd="pg_isready -U postgres" \
        --health-interval=5s \
        --health-timeout=5s \
        --health-retries=10 \
        \
        --entrypoint "docker-entrypoint.sh" \
        \
        $IMAGE \
            postgres \
                --timezone=America/Sao_Paulo \
                --log_timezone=America/Sao_Paulo \
                --max_connections=150 \
                --superuser_reserved_connections=3 \
                --shared_buffers=384MB \
                --effective_cache_size=768MB \
                --work_mem=4MB \
                --maintenance_work_mem=96MB \
                --temp_buffers=8MB \
                --wal_buffers=16MB \
                --min_wal_size=512MB \
                --max_wal_size=2GB \
                --checkpoint_completion_target=0.9 \
                --checkpoint_timeout=10min \
                --wal_compression=on \
                --wal_level=replica \
                --random_page_cost=1.1 \
                --effective_io_concurrency=200 \
                --default_statistics_target=100 \
                --max_worker_processes=4 \
                --max_parallel_workers=2 \
                --max_parallel_workers_per_gather=1 \
                --max_parallel_maintenance_workers=2 \
                --autovacuum=on \
                --autovacuum_max_workers=2 \
                --autovacuum_naptime=30s \
                --autovacuum_vacuum_threshold=50 \
                --autovacuum_vacuum_scale_factor=0.05 \
                --autovacuum_analyze_threshold=50 \
                --autovacuum_analyze_scale_factor=0.05 \
                --autovacuum_vacuum_cost_delay=2ms \
                --autovacuum_vacuum_cost_limit=400 \
                --statement_timeout=120000 \
                --idle_in_transaction_session_timeout=300000 \
                --lock_timeout=30000 \
                --log_destination=stderr \
                --logging_collector=off \
                --log_min_duration_statement=2000 \
                --log_line_prefix="%t [%p] %u@%d " \
                --log_checkpoints=on \
                --log_connections=off \
                --log_disconnections=off \
                --log_lock_waits=on \
                --log_temp_files=10240 \
                --log_autovacuum_min_duration=1000 \
                --shared_preload_libraries=pg_stat_statements \
                --track_activities=on \
                --track_counts=on \
                --track_io_timing=on \
                --track_functions=pl \
                --ssl=off \
                --port=5432 \
                --listen_addresses="*" \
                --max_wal_senders=3 \
                --hot_standby=on;

3.3 – Cache Redis/Valkey

O serviço RESP (Redis ou Valkey) serve de cache e mensageria entre workers do Waha. Vou utilizar o Valkey pela superioridade.

O nome do container será “waha-cache“:

Bash
# Variaveis
    NAME=waha-cache;

# Imagem
    IMAGE="valkey/valkey:9.1.0-trixie";
    docker pull $IMAGE;

# Pasta de persistencia
    DATADIR=/storage/$NAME;
    mkdir  -p $DATADIR;

# Renovar/rodar
    docker rm -f $NAME 2>/dev/null;
    docker run \
        -d \
        --restart            always \
        --name               $NAME \
        --hostname           $NAME.intranet.br \
        \
        --tmpfs /run:rw,noexec,nosuid,size=2m \
        --tmpfs /tmp:rw,noexec,nosuid,size=2m \
        --read-only \
        \
        --cpus                2.0 \
        --cpu-shares         1024 \
        --memory               2g \
        --memory-swap          2g \
        --memory-reservation   1g \
        \
        --network            network_public \
        --ip                 10.249.130.2 \
        \
        -v $DATADIR:/data \
        \
        --health-cmd="redis-cli ping" \
        --health-interval=1s \
        --health-timeout=3s \
        \
        $IMAGE \
            valkey-server \
                --bind '0.0.0.0 ::' \
                --port 6379 \
                --set-proc-title no \
                --tcp-backlog 8192 \
                --tcp-keepalive 30 \
                --timeout 0 \
                \
                --databases 16 \
                \
                --appendonly no \
                --dir /run \
                --save "" \
                --maxmemory-policy allkeys-lru;

3.4 – Storage S3 com Minio

Normalmente cada empresa centraliza o storage S3 na Amazon ou em um Minio unificado, aqui vou colocá-lo dedicado ao Waha.

Se você pretende participar de muitos grupos ou suas conversas envolvem com muitas imagens e videos, recomendo pelo menos 1 Terabyte de espaço para o volume do container. Tenha capacidade para expandir futuramente sem permitir que o serviço S3 fique sem espaço.

Personalize:

  • Crie os nomes de DNS (FQDN) para apontar para o IP do seu servidor/VPS:
    • Endereço do serviço S3: s3.seudominio.com.br;
    • Endereço de administração (console): minio.seudominio.com.br;
  • Usuário administrador: admin
  • Senha do administrador: tulipa@s3

Script:

Bash
# Variaveis
    NAME=minio;
    TZ="America/Sao_Paulo";

    # nomes de dns:
    # - s3 para usar
    FQDN_WEB="s3.seudominio.com.br";

    # - minio para administrar
    FQDN_CONSOLE="minio.seudominio.com.br";

    # Acesso de administrador
    MINIO_ROOT_USER="admin";
    MINIO_ROOT_PASSWORD="tulipa@s3";

    DATADIR=/storage/minio;
    mkdir -p $DATADIR;

# Atualizar/baixar imagem:
    IMAGE="quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z";
    docker pull $IMAGE;

# Renovar/Rodar:
    docker rm -f $NAME 2>/dev/null;
    docker run \
        -d \
        --restart            always \
        --name               $NAME \
        --hostname           $NAME.intranet.br \
        \
        --cpus                2.0 \
        --cpu-shares         1024 \
        --memory               2g \
        --memory-swap          2g \
        --memory-reservation   1g \
        \
        --network            network_public \
        --ip                 10.249.130.3 \
        \
        --tmpfs /run:rw,noexec,nosuid,size=2m \
        --tmpfs /tmp:rw,noexec,nosuid,size=2m \
        \
        -e TZ=$TZ \
        \
        -e "MINIO_ROOT_USER=$MINIO_ROOT_USER" \
        -e "MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD" \
        -e "MINIO_BROWSER_REDIRECT_URL=https://$FQDN_CONSOLE" \
        -e "MINIO_SERVER_URL=https://$FQDN_WEB" \
        \
        -v $DATADIR:/data \
        \
        --label "traefik.enable=true" \
        \
        --label "traefik.http.routers.${NAME}_web.rule=Host(\`$FQDN_WEB\`)" \
        --label "traefik.http.routers.${NAME}_web.entrypoints=web,websecure" \
        --label "traefik.http.routers.${NAME}_web.tls=true" \
        --label "traefik.http.routers.${NAME}_web.tls.certresolver=letsencrypt" \
        --label "traefik.http.routers.${NAME}_web.service=${NAME}_web" \
        --label "traefik.http.services.${NAME}_web.loadbalancer.server.port=9000" \
        --label "traefik.http.services.${NAME}_web.loadbalancer.passHostHeader=true" \
        \
        --label "traefik.http.routers.${NAME}_con.rule=Host(\`$FQDN_CONSOLE\`)" \
        --label "traefik.http.routers.${NAME}_con.entrypoints=web,websecure" \
        --label "traefik.http.routers.${NAME}_con.tls=true" \
        --label "traefik.http.routers.${NAME}_con.tls.certresolver=letsencrypt" \
        --label "traefik.http.routers.${NAME}_con.service=${NAME}_con" \
        --label "traefik.http.services.${NAME}_con.loadbalancer.server.port=9001" \
        --label "traefik.http.services.${NAME}_con.loadbalancer.passHostHeader=true" \
        \
        $IMAGE \
          server /data --console-address ":9001";

# Acesso:
    echo;
    echo "Acesso:";
    echo "Web......: https://$FQDN_WEB";
    echo "Console..: https://$FQDN_CONSOLE";
    echo;

Será necessário criar uma chave de acesso e um bucket (balde de arquivos) no S3/Minio para o Waha gravar e consultar objetos (imagens, audios, videos, documentos).

Acesse o endereço de console e siga os procedimentos abaixo:

  • No menu Administrator, clique em Buckets -> Create Bucket:
    • Preencha o campo Bucket Name com waha;
    • Clique no botão Create Bucket;
  • No menu User, clique em Access Keys -> Create Access Key:
    • Preencha o campo Access Key com waha_s3;
    • Preencha o campo Secret Key com um UUID ou senha segura, exemplo:
      • Secret Key: fce5f62b-9cb1-11f1-a065-000c2941d8a8
    • Preencha o campo Name com Waha API;
    • Clique no botão Create;

O Waha vai precisar:

  • Da URL do S3 (FQDN_WEB), s3.seudominio.com.br;
  • Do Bucket Name, Access Key e Secret Key.

4 – Instalando o Waha

Vamos nos preparar para entender e rodar o WAHA.

Você precisará criar um nome de DNS (FQDN) para acessar o Waha, crie algo como “waha.seudominio.com.br“.

4.1 – Variáveis de ambiente

Precisamos preencher as variáveis de ambiente que controlam o software internamente.

Variável de ambienteDescrição
TZTimezone do ambiente
REDIS_URLURL de acesso ao Redis ou Valkey
WHATSAPP_SESSIONS_POSTGRESQL_URLURL de acesso ao PostgreSQL
WAHA_WORKER_IDIdentificador do processo worker
WAHA_BASE_URLURL de acesso HTTPs ao Waha
WAHA_PUBLIC_URLURL de acesso HTTPs ao Waha
WAHA_APPS_CHATWOOT_LANGUAGES_FOLDERDiretório de template de idioma do padrão ChatWoot
WAHA_API_KEY_PLAINChave de API em texto plano (Authorization/Bearer)
WAHA_API_KEYHash Sha512 da chave de API na sintaxe “sha512:hash_sha512_aqui
WAHA_DASHBOARD_ENABLEDControle de ativação da dashboard web
WAHA_DASHBOARD_USERNAMENome de usuário da dashboard
WAHA_DASHBOARD_PASSWORDSenha para o login na dashboard
WHATSAPP_API_PORTPorta HTTP, padrão “3000”
WHATSAPP_API_HOSTNAMENome de DNS do servidor, “localhost” ou o FQDN “waha.seudominio.com.br”
WHATSAPP_API_SCHEMAProtocolo, padrão “HTTP”
WHATSAPP_DEFAULT_ENGINEMotor (engine) do WhatsApp, opções: “WEBJS”, “GOWS” ou “NOWEB”
WHATSAPP_SWAGGER_ENABLEDAtivar página de manual da API usando Swagger
WHATSAPP_SWAGGER_USERNAMEUsuário do Swagger
WHATSAPP_SWAGGER_PASSWORDSenha do Swagger
WHATSAPP_SWAGGER_TITLETítulo da página Swagger
WHATSAPP_SWAGGER_DESCRIPTIONDescrição da página Swagger
WHATSAPP_SWAGGER_EXTERNAL_DOC_URLURL da página Swagger
WAHA_MEDIA_STORAGEModo de armazenamento de arquivos, opções “S3”, “POSTGRESQL” ou “LOCAL”
WHATSAPP_FILES_LIFETIMETTL dos arquivos, padrão “0”
WHATSAPP_FILES_FOLDERCaminho da pasta para armazenamento tipo “LOCAL”
WAHA_S3_REGIONNome da região S3.
WAHA_S3_BUCKETNome do bucket (balde de arquivos)
WAHA_S3_ACCESS_KEY_IDChave de acesso (Access Key)
WAHA_S3_SECRET_ACCESS_KEYSenha da chave de acesso (Secret Key)
WAHA_S3_ENDPOINTURL do servidor S3 (s3.seudominio.com.br)
WAHA_S3_FORCE_PATH_STYLEUsar padrão com pasta, padrão “True”
WAHA_S3_PROXY_FILESFazer proxy, padrão “False”
WAHA_MEDIA_POSTGRESQL_URLURL de acesso PostgreSQL para armazenamento de arquivos para armazenamento tipo “POSTGRESQL”

4.2 – Chave de acesso da API Waha

Para usar a API do Waha é importante não errar a chave de acesso ao definir a variável de ambiente, vou dar um exemplo gerando a chave usando UUID:

Bash
# Gerar UUID randomico
    UUID=$(head -1 /proc/sys/kernel/random/uuid);
    echo "UUID gerado: $UUID";
    # exemplo de saida:
    #     UUID gerado: aad8a318-3c2d-4969-ab9e-43c9dcc4ca2a
    
# Gerar hash SHA512 do UUID:
    HASH=$(printf "$UUID" | sha512sum);
    echo "Hash: sha512:$HASH";
    # exemplo de saida:
    #     sha512:187863......915d49

No exemplo acima, temos:

  • aad8a318-3c2d-4969-ab9e-43c9dcc4ca2a” é o valor da env WAHA_API_KEY_PLAIN
  • sha512:187863……915d49” (eu cortei parte do hash com esses pontos pois a string é muito grande) é o valor da env WAHA_API_KEY

4.3 – Script para criar o container Waha

O nome do container será “waha-app“.

O controle de acesso por login será:

  • Usuário administrador: admin, envs:
    • WHATSAPP_SWAGGER_USERNAME
    • WAHA_DASHBOARD_USERNAME
  • Senha do administrador: Waha@Plus_2026, envs:
    • WHATSAPP_SWAGGER_PASSWORD
    • WAHA_DASHBOARD_PASSWORD

Script final:

Bash
# Variaveis
    NAME=waha-app;
    LOCAL=$NAME.intranet.br;
    FQDN_WEB="waha.seudominio.com.br";
    IMAGE="devlikeapro/waha:latest";
    
# Login de administracao Waha
    WAHA_LOGIN="admin";
    WAHA_PASSWORD="Waha@Plus_2026";

# Banco de dados Postgres
    PG_HOST="waha-database";
    PG_PORT="5432";
    PG_USER="postgres";
    PG_PASS="tulipasql";
    PG_DBNAME="waha";
    PG_OPT="sslmode=disable";

    WHATSAPP_SESSIONS_POSTGRESQL_URL="postgres://$PG_USER:$PG_PASS@$PG_HOST:$PG_PORT/$PG_DBNAME?$PG_OPT";

# Cache em RESP - Redis ou Valkey
    REDIS_URL="redis://waha-cache:6379";

# Whara Options
    WAHA_WORKER_ID=waha_app;
    WAHA_BASE_URL="https://$FQDN_WEB";
    WAHA_PUBLIC_URL="https://$FQDN_WEB";
    #WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER="/app/.chatwoot-languages";
    WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER="";

    # Chave de API
    # Versao SHA512 da chave de API (echo -n xxxx | sha512sum)
    WAHA_API_KEY_PLAIN="aad8a318-3c2d-4969-ab9e-43c9dcc4ca2a";
    HASH=$(printf $WAHA_API_KEY_PLAIN | sha512sum | awk '{print $1}');
    WAHA_API_KEY="sha512:$HASH";

    WAHA_RESTART_ALL_SESSIONS="true";
    WAHA_AUTO_START_DELAY_SECONDS="10";
    WAHA_PRESENCE_AUTO_ONLINE="True";
    WAHA_PRESENCE_AUTO_ONLINE_DURATION_SECONDS="25";

    WAHA_DASHBOARD_ENABLED="true"
    WAHA_DASHBOARD_USERNAME="$WAHA_LOGIN";
    WAHA_DASHBOARD_PASSWORD="$WAHA_PASSWORD";

    WAHA_APPS_ENABLED="true";
    WAHA_APPS_JOBS_CONCURRENCY="50";
    WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_AGE="259200";
    WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_COUNT="1000";
    WAHA_APPS_JOBS_REMOVE_ON_FAIL_AGE="2678400";
    WAHA_APPS_JOBS_REMOVE_ON_FAIL_COUNT="1000";

# Dados de whatsapp
    WHATSAPP_API_PORT="3000";
    WHATSAPP_API_HOSTNAME="$FQDN_WEB";
    WHATSAPP_API_SCHEMA="https";

    WHATSAPP_DEFAULT_ENGINE="GOWS";
    WHATSAPP_SWAGGER_ENABLED="true";
    WHATSAPP_SWAGGER_USERNAME="$WAHA_LOGIN";
    WHATSAPP_SWAGGER_PASSWORD="$WAHA_PASSWORD";
    WHATSAPP_SWAGGER_TITLE="Sua Empresa - WhatsApp HTTP API";
    WHATSAPP_SWAGGER_DESCRIPTION="API de integração WP";
    WHATSAPP_SWAGGER_EXTERNAL_DOC_URL="https://$FQDN_WEB";
    WHATSAPP_SWAGGER_VIDEO_EXAMPLE_URL="";
    WHATSAPP_SWAGGER_OPUS_EXAMPLE_URL="";
    WHATSAPP_SWAGGER_JPG_EXAMPLE_URL="";

# Armazenamento S3 Minio
    WAHA_MEDIA_STORAGE=S3;
    WAHA_S3_REGION=intranet1;
    WAHA_S3_BUCKET=waha;
    WAHA_S3_ACCESS_KEY_ID="waha_s3";
    WAHA_S3_SECRET_ACCESS_KEY="fce5f62b-9cb1-11f1-a065-000c2941d8a8";
    WAHA_S3_ENDPOINT="https://s3.seudominio.com.br";
    WAHA_S3_FORCE_PATH_STYLE=True;
    WAHA_S3_PROXY_FILES=False;


# Baixar imagem:
    docker pull $IMAGE;

# Pasta de persistencia
    DATADIR=/storage/$NAME;
    mkdir  -p $DATADIR/sessions;
    mkdir  -p $DATADIR/media;
    mkdir  -p $DATADIR/wp-data;
    mkdir  -p $DATADIR/wp-logs;

# Renovar/rodar
    docker rm -f $NAME 2>/dev/null;
    docker run \
        -d --restart=always \
        --name $NAME -h $LOCAL \
        --tmpfs /run:rw,noexec,nosuid,size=512m \
        --tmpfs /tmp:rw,noexec,nosuid,size=512m \
        --read-only \
        \
        --cpus                8.0 \
        --cpu-shares         2048 \
        --memory               4g \
        --memory-swap          4g \
        --memory-reservation   2g \
        \
        --network            network_public \
        --ip                 10.249.130.101 \
        \
        -p 3000:3000 \
        \
        -v $DATADIR/sessions:/app/.sessions \
        -v $DATADIR/media:/app/media \
        -v $DATADIR/wp-data:/app/waha_whatsapp_data \
        -v $DATADIR/wp-logs:/app/waha_whatsapp_logs \
        \
        -e TZ=America/Sao_Paulo \
        \
        -e REDIS_URL=$REDIS_URL \
        \
        -e WAHA_WORKER_ID=$WAHA_WORKER_ID \
        \
        -e WAHA_BASE_URL=$WAHA_BASE_URL \
        -e WAHA_PUBLIC_URL=$WAHA_PUBLIC_URL \
        -e WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER=$WAHA_APPS_CHATWOOT_LANGUAGES_FOLDER \
        -e WAHA_API_KEY_PLAIN=$WAHA_API_KEY_PLAIN \
        -e WAHA_API_KEY=$WAHA_API_KEY \
        \
        -e WAHA_DASHBOARD_ENABLED=$WAHA_DASHBOARD_ENABLED \
        -e WAHA_DASHBOARD_USERNAME=$WAHA_DASHBOARD_USERNAME \
        -e WAHA_DASHBOARD_PASSWORD=$WAHA_DASHBOARD_PASSWORD \
        \
        -e WHATSAPP_SESSIONS_POSTGRESQL_URL=$WHATSAPP_SESSIONS_POSTGRESQL_URL \
        \
        -e WHATSAPP_API_PORT=$WHATSAPP_API_PORT \
        -e WHATSAPP_API_HOSTNAME=$WHATSAPP_API_HOSTNAME \
        -e WHATSAPP_API_SCHEMA=$WHATSAPP_API_SCHEMA \
        \
        -e WHATSAPP_DEFAULT_ENGINE=$WHATSAPP_DEFAULT_ENGINE \
        -e WHATSAPP_SWAGGER_ENABLED=$WHATSAPP_SWAGGER_ENABLED \
        -e WHATSAPP_SWAGGER_USERNAME=$WHATSAPP_SWAGGER_USERNAME \
        -e WHATSAPP_SWAGGER_PASSWORD=$WHATSAPP_SWAGGER_PASSWORD \
        -e WHATSAPP_SWAGGER_TITLE="$WHATSAPP_SWAGGER_TITLE" \
        -e WHATSAPP_SWAGGER_DESCRIPTION="$WHATSAPP_SWAGGER_DESCRIPTION" \
        -e WHATSAPP_SWAGGER_EXTERNAL_DOC_URL=$WHATSAPP_SWAGGER_EXTERNAL_DOC_URL \
        -e WHATSAPP_SWAGGER_VIDEO_EXAMPLE_URL=$WHATSAPP_SWAGGER_VIDEO_EXAMPLE_URL \
        -e WHATSAPP_SWAGGER_OPUS_EXAMPLE_URL=$WHATSAPP_SWAGGER_OPUS_EXAMPLE_URL \
        -e WHATSAPP_SWAGGER_JPG_EXAMPLE_URL=$WHATSAPP_SWAGGER_JPG_EXAMPLE_URL \
        \
        -e WAHA_RESTART_ALL_SESSIONS=$WAHA_RESTART_ALL_SESSIONS \
        -e WAHA_AUTO_START_DELAY_SECONDS=$WAHA_AUTO_START_DELAY_SECONDS \
        -e WAHA_PRESENCE_AUTO_ONLINE=$WAHA_PRESENCE_AUTO_ONLINE \
        -e WAHA_PRESENCE_AUTO_ONLINE_DURATION_SECONDS=$WAHA_PRESENCE_AUTO_ONLINE_DURATION_SECONDS \
        \
        -e WAHA_APPS_ENABLED=$WAHA_APPS_ENABLED \
        -e WAHA_APPS_JOBS_CONCURRENCY=$WAHA_APPS_JOBS_CONCURRENCY \
        -e WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_AGE=$WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_AGE \
        -e WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_COUNT=$WAHA_APPS_JOBS_REMOVE_ON_COMPLETE_COUNT \
        -e WAHA_APPS_JOBS_REMOVE_ON_FAIL_AGE=$WAHA_APPS_JOBS_REMOVE_ON_FAIL_AGE \
        -e WAHA_APPS_JOBS_REMOVE_ON_FAIL_COUNT=$WAHA_APPS_JOBS_REMOVE_ON_FAIL_COUNT \
        \
        -e WAHA_MEDIA_STORAGE=$WAHA_MEDIA_STORAGE \
        -e WAHA_S3_REGION=$WAHA_S3_REGION \
        -e WAHA_S3_BUCKET=$WAHA_S3_BUCKET \
        -e WAHA_S3_ACCESS_KEY_ID=$WAHA_S3_ACCESS_KEY_ID \
        -e WAHA_S3_SECRET_ACCESS_KEY=$WAHA_S3_SECRET_ACCESS_KEY \
        -e WAHA_S3_ENDPOINT=$WAHA_S3_ENDPOINT \
        -e WAHA_S3_FORCE_PATH_STYLE=$WAHA_S3_FORCE_PATH_STYLE \
        -e WAHA_S3_PROXY_FILES=$WAHA_S3_PROXY_FILES \
        \
        \
        --label "traefik.enable=true" \
        \
        --label "traefik.http.routers.$NAME.rule=Host(\`$FQDN_WEB\`)" \
        --label "traefik.http.routers.$NAME.entrypoints=web,websecure" \
        --label "traefik.http.routers.$NAME.tls=true" \
        --label "traefik.http.routers.$NAME.tls.certresolver=letsencrypt" \
        --label "traefik.http.routers.$NAME.service=$NAME" \
        \
        --label "traefik.http.middlewares.${NAME}sslheader.headers.customrequestheaders.X-Forwarded-Proto=https" \
        --label "traefik.http.routers.$NAME.middlewares=${NAME}sslheader@docker" \
        \
        --label "traefik.http.services.$NAME.loadbalancer.server.port=$WHATSAPP_API_PORT" \
        --label "traefik.http.services.$NAME.loadbalancer.passHostHeader=true" \
        \
        \
        $IMAGE \
            node ./dist/main.js;

# Acesso:
    echo;
    echo "Acesso:";
    echo "Web......: https://$FQDN_WEB";
    echo "Admin....: https://$FQDN_WEB/dashboard/";
    echo;

5 – Primeiro acesso

Vamos acessar o Waha e aprender como usar e registrar contas do WhatsApp.

5.1 – Swagger, manual da API

Acesse a URL do Waha (waha.seudominio.com.br), esse primeiro login é o acesso do Swagger (uma página de manual, exemplos de testes da API).

5.2 – Dashboard e gestão

Agora vamos colocar o Waha pra funcionar em uma conta do WhatsApp.

Você precisará acessar o path /dashboard da URL, exemplo https://waha.seudominio.com.br/dashboard

Tela inicial (sem contas ainda, ignore o erro inicial):

5.3 – Configurando o worker

Antes de iniciar uma sessão usando sua conta no WhatsApp, precisamos colocar o Worker padrão online.

Edite o worker padrão clicando no ícone do lápis:

Na edição, informe:

  • Em “Name“, preencha com o mesmo valor que você definiu na variável WAHA_WORKER_ID, no nosso exemplo (e pode deixar assim) é waha_app;
  • Em “API URL“, defina a URL do nosso servidor (exemplo https://waha.seudominio.com.br);
  • Em API Key, preencha com a chave de API que você definiu na variável WAHA_API_KEY_PLAIN, no nosso exemplo é aad8a318-3c2d-4969-ab9e-43c9dcc4ca2a
  • Click no botão “Save

Agora o worker waha_app consegue usar a própria API interna para processar eventos. Observe que o worker fica online. Você pode reiniciar o worker em casos de erros iniciais.

5.4 – Criando uma sessão WhatsApp Web no Waha

Agora que temos o worker pra trabalhar nas sessões, vamos criar uma sessão de WhatsApp Web para que o Waha tenha acesso à nossa conta.

Na parte de “Sessions” não há contas ainda, vamos iniciar uma sessão usando o QR-CODE do WhatsApp Web. Clique no botão “Start New“.

No formulário da sessão, apenas crie-a clicando no botão “Create“. O nome é opcional mas recomendado.

Com a sessão criada, clique no botão de “Start” (ícone de play) para iniciar a conexão com o servidor da Meta.

Quando a conexão inicial da sessão for iniciada com a Meta, a sessão entra em modo “SCAN_QR_CODE“, clique no botão “Screenshot / QR” para exibir o QR-CODE.
Leia-o pelo aplicativo do WhatsApp do seu smartphone.

Ao ler o QR-CODE o Waha iniciará a sincronização da sua conta WhatsApp. Em alguns minutos a sessão estará pronta para uso e com modo “WORKING“:

Pronto! O Waha está captando em tempo real todos os dados de chats, grupos, contatos e totalmente capaz de interagir no WhatsApp.

6 – Webhook

Cada sessão suporta chamadas de webhook para eventos. Se alguem te mandar uma mensagem direta isso dispara a chamada de uma URL de sua escolha.

Sua webhook será o ponto de entrada para que seus agentes de IA e automações possam reagir aos eventos da sessão. Fluxo de tráfego da mensagem:

Fluxo de tráfego da mensagem desde o servidor WP/META até o Waha (1), processando na sessão (2) e o acionamento da webhook da sessão (3) para encaminhamento da mensagem em JSON. O sistema que provê a URL do webhook recebe a mensagem (4) e faz o processamento personalizado, podendo acionar novamente a API do Waha (5) para responder a mensagem (6).

Para configurar a webhook, vá nas configurações da sessão no botão roxo “Configuration“:

Encontre a parte de “Webhooks” na configuração e informe a URL que será invocada para os “Events” (eventos) selecionados:

Recomendo o N8N para construção do protótipo dos seus agentes, e mais tarde você pode transformá-lo em códigos performáticos (Python, Go, Rust, Node, …).

7 – Integração com ChatWoot

O ChatWoot é um software omni-channel de sistemas de mensagens. O Waha possui integração nativa com caixas de entrada do tipo API no ChatWoot.

Ao conectar o Waha no ChatWoot, você permite que inúmeros atendentes e agentes de IA funcionem sem precisar desenvolver webhooks e agentes manualmente.

x

(falta:, integração com ChatWoot)

x

Terminamos por hoje!

Patrick Brandão, patrickbrandao@gmail.com

Todo mundo pensa em mudar o mundo,
mas ninguém pensa em mudar a si mesmo.
Leon Tolstói