Documentação / Evolution API Endpoints

Evolution API Endpoints

Entrar

Evolution API Endpoints

Documentação completa dos endpoints da Evolution API utilizados no projeto pilot-status-nextjs-typescript-frontend-backend.

Baseado em: packages/evolution/src/


Sumário


Instance Endpoints

1. Create Instance

Endpoint: POST /instance/create

Descrição: Cria uma nova instância do WhatsApp.

Request Body:

{
  instanceName: string;           // Nome da instância
  number: string;                 // Número do assinante (apenas dígitos, sem "+")
  qrcode?: boolean;               // Default: true - Gerar QR Code
  integration?: string;            // Default: "WHATSAPP-BAILEYS"
  syncFullHistory?: boolean;      // Default: false
  proxyFields?: Record<string, unknown>;  // Campos de proxy (proxyHost, etc.)
  extra?: Record<string, unknown>;        // Campos extras
}

Response:

{
  raw: unknown;                    // Resposta JSON bruta
  state: string;                   // Estado da instância (ex: "open", "close", "connecting")
  instanceApiKey: string | null;  // API key da instância ("hash")
  qrcodeBase64: string | null;     // QR Code em base64
  pairingCode: string | null;      // Código de pareamento
}

2. Connect Instance

Endpoint: GET /instance/connect/:instanceName

Descrição: Conecta uma instância existente e retorna o QR Code.

Query Params:

  • number (opcional): Número do WhatsApp

Response:

{
  raw: unknown;
  qrcodeBase64: string | null;
  pairingCode: string | null;
}

3. Connection State

Endpoint: GET /instance/connectionState/:instanceName

Descrição: Obtém o estado atual da conexão da instância.

Response:

{
  state: string | null;      // "open" | "close" | "connecting" | "not_found"
  instanceName: string | null;
  raw: unknown;
}

4. Set Presence

Endpoint: POST /instance/setPresence/:instanceName

Descrição: Define o estado de presença da instância (disponível, digitando, etc.).

Request Body:

{
  presence: "available" | "unavailable" | "composing" | "recording" | "paused";
  number?: string;     // Chat JID quando scoping para conversa específica
  delay?: number;      // Milissegundos para manter a presença
}

Response:

{
  status: number;
  bodyText: string;
  raw: unknown;
  isConnectedSignal: boolean;  // true quando status=201 e body não contém "close"
}

5. Restart Instance

Endpoint: POST /instance/restart/:instanceName

Descrição: Reinicia uma instância.

Response:

{
  raw: unknown;
  state: string | null;
  instanceName: string | null;
  hasReconnectBundle: boolean;  // true quando body inclui payload QR (disconnect real)
  isObjectObjectSignal: boolean;  // true quando Evolution retorna erro "[object Object]"
  bodyText: string;
}

6. Logout Instance

Endpoint: DELETE /instance/logout/:instanceName

Descrição: Faz logout de uma instância.

Response: void (no content on success)


7. Delete Instance

Endpoint: DELETE /instance/delete/:instanceName

Descrição: Deleta uma instância.

Response: void (no content on success)


Message Endpoints

1. Send Text Message

Endpoint: POST /message/sendText/:instanceName

Descrição: Envia uma mensagem de texto.

Request Body:

{
  number: string;        // Número do destinatário
  text: string;          // Conteúdo da mensagem
  linkPreview?: boolean; // Habilita preview de link quando texto contém URL http(s)
}

Response:

{
  raw: unknown;
  keyId: string | null;      // Evolution key.id da mensagem enviada
  instanceId: string | null;
  state: string | null;      // instance.state no body (útil para detectar "close")
  bodyText: string;
}

2. Send Buttons Message

Endpoint: POST /message/sendButtons/:instanceName

Descrição: Envia uma mensagem com botões interativos.

Request Body:

{
  number: string;
  title: string;
  description: string;
  footer?: string;
  buttons: Array<Record<string, unknown>>;
  thumbnailUrl?: string;
}

Response:

{
  raw: unknown;
  keyId: string | null;
  instanceId: string | null;
  state: string | null;
  bodyText: string;
}

3. Send Media Message

Endpoint: POST /message/sendMedia/:instanceName

Descrição: Envia uma mensagem com mídia (imagem, vídeo, documento).

Request Body:

{
  number: string;
  mediatype: "image" | "video" | "document" | string;
  mimetype: string;
  media: string;         // URL ou base64
  caption?: string;
  fileName?: string;
}

Response:

{
  raw: unknown;
  keyId: string | null;
  instanceId: string | null;
  state: string | null;
  bodyText: string;
}

Chat Endpoints

1. Send Presence

Endpoint: POST /chat/sendPresence/:instanceName

Descrição: Envia sinal de presença para um chat (ex: digitando).

Request Body:

{
  presence: "available" | "unavailable" | "composing" | "recording" | "paused";
  number?: string;     // Chat JID específico
  delay?: number;      // Milissegundos para manter o sinal
}

Response:

{
  status: number;
  bodyText: string;
  raw: unknown;
  isConnectedSignal: boolean;  // true quando status=200/201 e body não contém "close"
}

Uso típico: Simular "digitando" antes de enviar mensagem:

// Calcula delay baseado no tamanho do texto: 600 + (tamanho * 35)ms
const typingDelayMs = Math.max(800, Math.min(5000, 600 + text.length * 35));

2. Find Status Message

Endpoint: POST /chat/findStatusMessage/:instanceName

Descrição: Busca histórico de status de mensagens por (remoteJid, keyId).

Request Body:

{
  where: {
    remoteJid: string;
    id: string;           // evolutionKeyId
  };
  page?: number;          // Default: 1
  offset?: number;        // Default: 10
}

Response:

Array<{
  status?: string;
  keyId?: string;
  remoteJid?: string;
}>

3. WhatsApp Numbers Info

Endpoint: POST /chat/whatsappNumbers/:instanceName

Descrição: Retorna informações sobre números de WhatsApp consultados.

Request Body:

{
  numbers: string[];  // Lista de números para consultar
}

Response:

Array<{
  number?: string;
  exists?: boolean;
  name?: string;
}>

Group Endpoints

1. Find Group Info

Endpoint: GET /group/findGroupInfos/:instanceName

Descrição: Obtém informações sobre um grupo.

Query Params:

  • groupJid (requerido): JID do grupo
  • participants (opcional): Default "false" - Incluir participantes

Response:

{
  subject: string | null;  // Nome do grupo
  raw: unknown;
}

Evolution Webhooks

Webhook Endpoint

Endpoint: POST /api/internal/webhook

Descrição: Recebe webhooks da Evolution API e processa eventos de mensagens e conexão.

Headers:

  • Content-Type: application/json

Envelope Base:

interface EvolutionWebhookEnvelope {
    date_time?: string;      // ISO timestamp da Evolution
    sender?: string;         // Remetente
    server_url?: string;     // URL do servidor Evolution
    apikey?: string;         // API key
    destination?: string;    // Destinatário
}

1. Connection Update

Event: connection.update

Descrição: Recebido quando o estado da conexão da instância muda.

Propriedades Necessárias (Pilot Status):

{
    event: "connection.update";
    instance: string;        // Nome da instância
    data: {
        state?: string;          // "open" | "close" | "connecting"
        instance?: string;
        statusReason?: number;
    };
}

O que a Pilot Status faz:

  1. Atualiza o estado da instância no banco de dados (whatsappInstance.state)
  2. Detecta transição de OPEN para CONNECTING e inicia loop de recuperação
  3. Marca firstConnectedAt na primeira conexão bem-sucedida
  4. Enfileira validação de instância após 15 segundos
  5. Envia alertas Pushover quando a instância é desconectada ou conectada pela primeira vez
  6. Envia notificações WhatsApp para usuários quando a instância é desconectada
  7. Despacha evento number.connected para webhooks do cliente

Regras de Notificação:

  • Conexão: envia alerta em qualquer primeiro sucesso
  • Desconexão: envia apenas se estado for "close" e statusReason não for 401
  • Logout: envia alerta de deslogado

2. Logout Instance

Event: logout.instance

Descrição: Recebido quando a instância é deslogada do WhatsApp.

Propriedades Necessárias (Pilot Status):

{
    event: "logout.instance";
    instance: string;        // Nome da instância
    data: unknown;
}

O que a Pilot Status faz:

  1. Define estado da instância como "LOGOUT"
  2. Envia alerta Pushover para desenvolvedores
  3. Envia notificação WhatsApp para usuários com texto personalizado
  4. Limpa cache de alertas de desconexão em Redis

Exemplo de Notificação WhatsApp:

Atenção: sua conexão WhatsApp "{displayName}" foi deslogada e precisa ser reconectada na Pilot Status.

3. Messages Upsert

Event: messages.upsert

Descrição: Recebido quando uma nova mensagem é criada ou atualizada (enviada ou recebida).

Propriedades Necessárias (Pilot Status):

{
    event: "messages.upsert";
    instance: string;
    data: {
        key?: {
            id?: string;           // ID único da mensagem (evolutionKeyId)
            fromMe?: boolean;      // true se enviado por nós
            remoteJid?: string;    // JID do remetente/destinatário
            participant?: string;   // Para grupos, participante
            participantAlt?: string;
            remoteJidAlt?: string;
            addressingMode?: "lid" | "pn";
        };
        source?: string;
        status?: string;
        message?: Record<string, unknown>;  // Conteúdo da mensagem
        pushName?: string;                 // Nome do contato
        instanceId?: string;
        messageType?: string;              // "conversation", "image", etc.
        messageTimestamp?: number;
    };
}

O que a Pilot Status faz:

Para Mensagens de Grupo:

  1. Detecta se é mensagem de grupo (remoteJid contém @g.us)
  2. Extrai informações do participante (digits ou LID)
  3. Busca nome do grupo via API Evolution (com cache)
  4. Despacha webhook message.group para clientes configurados
  5. Persiste identidade WhatsApp para LIDs
  6. Ignora mensagens de grupos não "conversation"
  7. Ignora mensagens da instância do Pilot Status

Para Mensagens Diretas:

  1. Persiste JIDs da Evolution: Salva mapeamento evolutionKeyId -> [remoteJid, remoteJidAlt]
  2. Extrai conteúdo: Texto, mídia, botões, etc.
  3. Detecta respostas: Verifica se mensagem é quote/reply
  4. Correlaciona com mensagens enviadas: Busca última mensagem enviada para o mesmo contato
  5. Detecta opt-in: Busca token optin <slug> na mensagem
  6. Registra opt-in: Cria/atualiza registro em projectWhatsAppOptIn
  7. Despacha webhooks de opt-in: Evento optin.created para clientes
  8. Trata botão de reconexão: Intercepa resposta a notificação de desconexão
  9. Despacha webhooks de mensagem:
    • message.sent (se fromMe=true)
    • message.reply (se é resposta)
    • message.received (se é mensagem recebida)
  10. Aplica redação de PII: Remove dados sensíveis após período de retenção
  11. Persiste mensagens inbound: Guarda em IncomingMessage para debug

Fluxo de Opt-In:

Mensagem contém "optin <token>" 
→ Busca projeto por slug ou ID 
→ Cria registros de opt-in para todas as variantes de número 
→ Atualiza lastSeenAt 
→ Despacha webhooks optin.created para todos os endpoints configurados

Tratamento de Botão de Reconexão:

// Botão ID: RECONNECT_BTN_ID
// Correlação: RECONNECT_CORRELATION_KEY_PREFIX + <messageId>
// Ação: Enfileira nova notificação de desconexão

4. Messages Update

Event: messages.update

Descrição: Recebido quando o status de uma mensagem muda (SENT, DELIVERED, READ, FAILED).

Propriedades Necessárias (Pilot Status):

{
    event: "messages.update";
    instance: string;
    data: {
        keyId?: string;           // ID da mensagem (evolutionKeyId)
        fromMe?: boolean;
        status?: string;          // "PENDING", "SERVER_ACK", "DELIVERED", "READ", "ERROR"
        messageId?: string;       // ID interno da mensagem
        remoteJid?: string;
        instanceId?: string;
    };
    date_time?: string;           // Timestamp do evento
}

Mapeamento de Status:

STATUS_MAP = {
    "PENDING": "PENDING",
    "SERVER_ACK": "SENT",
    "DELIVERED": "DELIVERED",
    "READ": "READ",
    "ERROR": "FAILED",
}

O que a Pilot Status faz:

  1. Busca mensagem no banco:

    • Por id (messageId interno)
    • Por evolutionKeyId
    • Por evolutionKeyId + evolutionInstanceId
    • Por evolutionKeyId (fallback)
  2. Valida atualização: Previne regressões de status (ex: READ → SENT)

  3. Atualiza timestamps:

    • sentAt para status SENT
    • deliveredAt para status DELIVERED
    • readAt para status READ
    • failedAt para status FAILED
  4. Persiste informações de erro:

    • errorMessage: Mensagem amigável
    • internalErrorMessage: Detalhes técnicos
  5. Persiste JIDs: Salva remoteJid e remoteJidAlt

  6. Registra no MessageTrace:

    • EVOLUTION_STATUS_UPDATE_RECEIVED: Evento recebido
    • MESSAGE_STATUS_UPDATED: Status atualizado no banco
  7. Enfileira verificação de delivery:

    • Quando status muda para SENT
    • Job sent-delivery-check após 60 segundos
    • 3 tentativas com backoff fixo de 30 segundos
  8. Despacha webhooks do cliente:

    • message.failed: Quando status é FAILED e webhook está ativo
    • message.sent: Quando status é SENT, anterior não era SENT, e fromMe=true
    • message.delivered: Quando status é DELIVERED e anterior não era DELIVERED
    • message.read: Quando status é READ e anterior não era READ
  9. Respeita redação de PII: Remove dados sensíveis após retenção

  10. Respeita subscrições de instância: Verifica se webhook está inscrito para a instância

Dispatch de Webhook (Quando mensagem não encontrada): Se não encontrar mensagem no banco, faz dispatch de nível tenant:

{
    event: "message.sent" | "message.delivered" | "message.read" | "message.failed",
    data: {
        messageId: keyId || messageId,
        internalMessageId: null,
        destinationNumber: "+55...",
        status: "SENT" | "DELIVERED" | "READ" | "FAILED",
        sentAt?: ISO_STRING,
        deliveredAt?: ISO_STRING,
        readAt?: ISO_STRING,
        failedAt?: ISO_STRING,
        errorMessage?: string,
    }
}

Eventos Internos de Webhook do Cliente

A Pilot Status despacha os seguintes eventos para webhooks dos clientes:

message.received

Triggerado quando: Mensagem recebida (não enviada por nós, não é resposta)

Payload:

{
    event: "message.received",
    data: {
        fromMe: boolean;
        from: string;           // E.164 ou LID
        fromName: string | null;
        destinationNumber: string;
        content: string;
        receivedAt: string;     // ISO timestamp
        messageId: string;       // Evolution keyId
        correlationId?: string;
        environment: "TEST" | "LIVE";
        whatsappInstanceName?: string;
    }
}

message.reply

Triggerado quando: Mensagem é resposta a mensagem enviada anteriormente

Payload:

{
    event: "message.reply",
    data: {
        from: string;
        fromName: string | null;
        destinationNumber: string;
        content: string;         // Conteúdo da mensagem respondida
        replyContent: string;     // Conteúdo da resposta
        receivedAt: string;
        messageId: string;
        quotedMessageId: string;  // Evolution keyId da mensagem respondida
        messageRepliedId?: string; // ID interno da mensagem respondida
        correlationId?: string;
        environment: "TEST" | "LIVE";
        buttonId?: string;       // Se resposta de botão
    }
}

message.sent

Triggerado quando: Mensagem enviada atinge status SENT

Payload:

{
    event: "message.sent",
    data: {
        messageId: string;       // Evolution keyId
        internalMessageId?: string; // ID interno
        correlationId?: string;
        environment: "TEST" | "LIVE";
        destinationNumber: string;
        content: string;
        status: "SENT";
        sentAt: string;
    }
}

message.delivered

Triggerado quando: Mensagem atinge status DELIVERED

Payload:

{
    event: "message.delivered",
    data: {
        messageId: string;
        internalMessageId?: string;
        correlationId?: string;
        environment: "TEST" | "LIVE";
        destinationNumber: string;
        content: string;
        status: "DELIVERED";
        deliveredAt: string;
    }
}

message.read

Triggerado quando: Mensagem atinge status READ

Payload:

{
    event: "message.read",
    data: {
        messageId: string;
        internalMessageId?: string;
        correlationId?: string;
        environment: "TEST" | "LIVE";
        destinationNumber: string;
        content: string;
        status: "READ";
        readAt: string;
    }
}

message.failed

Triggerado quando: Mensagem atinge status FAILED

Payload:

{
    event: "message.failed",
    data: {
        messageId: string;
        internalMessageId?: string;
        correlationId?: string;
        environment: "TEST" | "LIVE";
        destinationNumber: string;
        content: string;
        status: "FAILED";
        failedAt: string;
        errorMessage: string;
    }
}

message.group

Triggerado quando: Mensagem recebida em grupo

Payload:

{
    event: "message.group",
    data: {
        fromNumber: string;     // E.164 ou LID do participante
        fromName: string | null;
        fromMe: boolean;
        groupName: string | null;
        content: string;
        messageId: string;
        groupId: string;        // JID do grupo
        happenedAt: string;
        environment: "TEST" | "LIVE";
    }
}

optin.created

Triggerado quando: Usuário envia mensagem contendo "optin <token>"

Payload:

{
    event: "optin.created",
    data: {
        projectId: string;
        environment: "TEST" | "LIVE";
        whatsappInstanceName: string;
        destinationNumber: string;
        destinationHash: string;
        fromName: string | null;
        isFirstOptIn: boolean;
        firstOptInAt: string;
        lastSeenAt: string;
    }
}

Ingestão Dual (Webhook + RabbitMQ)

A Pilot Status implementa um sistema de ingestão dual para garantir processamento confiável:

Webhook Primário:

  • Endpoint: POST /api/internal/webhook
  • Uso: Processamento imediato de webhooks Evolution
  • Deduplicação: Redis (chave por evento)

RabbitMQ Secundário:

  • Queue: evolution_events
  • Uso: Consumidor processa eventos como backup/reconexão
  • Deduplicação: Redis compartilhado

Dual Ingestion Manager:

  • Coordenador dual-ingestion.ts
  • Sistema de locks para evitar processamento duplo
  • Finaliza ingestão ao término do processamento

Propriedades da Evolution Necessárias para Pilot Status

| Propriedade | Uso na Pilot Status | Obrigatório | |------------|---------------------|-------------| | event | Roteamento para handler correto | ✅ Sim | | instance | Identificar instância no banco | ✅ Sim | | date_time | Timestamp do evento | ❌ Não | | data.key.id | Correlação com mensagens enviadas | ✅ Sim | | data.key.fromMe | Diferenciar enviado vs recebido | ✅ Sim | | data.key.remoteJid | Identificar contato | ✅ Sim | | data.message | Conteúdo da mensagem | ✅ Sim | | data.pushName | Nome do contato | ❌ Não | | data.messageType | Tipo de mensagem | ❌ Não | | data.status | Status da mensagem | ✅ Sim (messages.update) | | data.state | Estado da conexão | ✅ Sim (connection.update) |


Headers Especiais

Verification (Opcional):

X-Evolution-Signature: sha256=<hmac>

Dual Ingestion Headers:

X-Ingestion-Source: WEBHOOK | RABBITMQ
X-Dedup-Key: <unique_key>

Common Types

EvolutionConnectionState

type EvolutionConnectionState =
  | "open"
  | "close"
  | "connecting"
  | "not_found"
  | string;

InstancePresence

type InstancePresence =
  | "available"
  | "unavailable"
  | "composing"
  | "recording"
  | "paused";

EvolutionCallOptions

interface EvolutionCallOptions {
  apiKey?: string;              // Override global apikey header (per-instance key)
  signal?: AbortSignal;          // Para cancelamento/timeout
  fetchImpl?: typeof fetch;      // Fetch injetado (tests)
  baseUrl?: string;              // Override base URL (tests)
}

Headers & Authentication

Todos os endpoints exigem o header de autenticação:

apikey: <EVOLUTION_API_KEY>

Autenticação por Instância

Alguns endpoints suportam override da API key por instância:

apiKey: opts.apiKey ?? getEvolutionApiKey();

Error Handling

EvolutionHttpError

Lançado em respostas não-2xx:

{
  status: number;
  bodyText: string;
  url: string;
  isInstanceNotFound?: boolean;  // true quando status indica instância não encontrada
}

EvolutionInvalidJsonError

Lançado quando a API retorna JSON inválido ou vazio.

EvolutionNotConfiguredError

Lançado quando EVOLUTION_API_URL ou EVOLUTION_API_KEY não estão configurados.


URL Base

A URL base da API é configurada via:

  • Variável de ambiente: EVOLUTION_API_URL
  • Variável de ambiente: EVOLUTION_API_KEY

Exemplo de URL completa:

https://api.evolution.com/instance/create

Observações Importantes

  1. Instance Name: O nome da instância deve ser URL-encoded nos endpoints.
  2. Proxies: O suporte a proxies é configurável via proxyFields no create e setupProxy() no fetch.
  3. Timeouts: Use signal (AbortSignal) para controlar timeouts.
  4. Retries: Implementar retries com backoff exponencial é recomendado.
  5. Error Signals: Alguns erros especiais como [object Object] são tratados internamente.

Testes

Os testes unitários e de integração podem ser encontrados em:

  • packages/evolution/test/v2/
  • packages/evolution/test/webhook/

Referências

  • Código fonte: packages/evolution/src/
  • Tipos: packages/evolution/src/types.ts
  • Provider: packages/evolution/src/provider.ts
  • HTTP client: packages/evolution/src/http.ts
  • Webhook types: packages/evolution/src/webhook/events.ts
  • Webhook schema: packages/evolution/src/webhook/schema.ts
  • Webhook handlers: apps/fullstack/src/app/api/internal/webhook/handlers/
  • Connection handler: apps/fullstack/src/app/api/internal/webhook/handlers/connection.ts
  • Messages upsert handler: apps/fullstack/src/app/api/internal/webhook/handlers/messages-upsert.ts
  • Messages update handler: apps/fullstack/src/app/api/internal/webhook/handlers/messages-update.ts
  • Webhook route: apps/fullstack/src/app/api/internal/webhook/route.ts
  • Dual ingestion: apps/fullstack/src/app/api/internal/webhook/dual-ingestion.ts