Pular para conteúdo

TWILIO CALL MAKE - Realizar Chamadas Telefônicas Automáticas

O que é este Node?

O TWILIO CALL MAKE é o node responsável por iniciar chamadas telefônicas automatizadas usando a API do Twilio. Ele permite ligar para qualquer número no mundo e executar scripts TwiML (Twilio Markup Language) que controlam o que acontece durante a chamada.

Por que este Node existe?

Automação de chamadas é essencial para muitos casos de uso. O TWILIO CALL MAKE existe para:

  1. Notificações Urgentes: Alertar pessoas sobre emergências que exigem resposta imediata
  2. IVR Automatizado: Criar sistemas de resposta interativa por voz (menus telefônicos)
  3. Confirmações Críticas: Verificar agendamentos importantes com interação humana
  4. Pesquisas por Voz: Coletar feedback através de chamadas automatizadas
  5. Lembretes Personalizados: Avisos que exigem atenção garantida (mais efetivo que SMS)

Como funciona internamente?

Quando o TWILIO CALL MAKE é executado, o sistema:

  1. Valida Credenciais: Verifica se accountSid e authToken estão configurados
  2. Processa Variáveis: Substitui variáveis em from, to, twimlUrl e statusCallback
  3. Prepara Parâmetros: Cria formulário URL-encoded com dados da chamada
  4. Adiciona Callback (opcional): Se statusCallback estiver presente, Twilio notificará status da chamada
  5. Inicia Chamada: Faz POST para https://api.twilio.com/2010-04-01/Accounts/{accountSid}/Calls.json
  6. Twilio Executa TwiML: Twilio busca o XML no twimlUrl e executa instruções (falar, gravar, coletar dígitos)
  7. Retorna Resposta: Salva callSid, status, to, from na variável especificada
  8. Se erro: Lança exceção com detalhes do problema

Código interno (twilio.executor.ts:104-149):

private async executeCallOperation(
  operation: string,
  config: TwilioConfig,
  data: any,
  context: ExecutionContext,
): Promise<any> {
  switch (operation) {
    case 'make':
      const from = this.replaceVariables(data.from, context.variables);
      const to = this.replaceVariables(data.to, context.variables);
      const url = this.replaceVariables(data.twimlUrl, context.variables);

      const params = new URLSearchParams();
      params.append('From', from);
      params.append('To', to);
      params.append('Url', url);

      if (data.statusCallback) {
        params.append('StatusCallback', this.replaceVariables(data.statusCallback, context.variables));
      }

      const response = await axios.post(
        `${this.baseUrl}/Accounts/${config.accountSid}/Calls.json`,
        params,
        {
          auth: {
            username: config.accountSid,
            password: config.authToken,
          },
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
        },
      );

      return {
        callSid: response.data.sid,
        status: response.data.status,
        to: response.data.to,
        from: response.data.from,
      };

    default:
      throw new Error(`Unknown call operation: ${operation}`);
  }
}

Quando você DEVE usar este Node?

Use TWILIO CALL MAKE sempre que precisar de chamadas telefônicas automatizadas programáveis:

Casos de uso

  1. Sistema de Alertas: "Ligar para equipe de TI quando servidor crítico cair"
  2. Confirmação de Consultas: "Ligar para pacientes 24h antes da consulta e coletar confirmação via DTMF"
  3. Pesquisa de Satisfação: "Ligar para clientes pós-compra e coletar nota de 0-10"
  4. Recuperação de Carrinho: "Ligar para clientes que abandonaram carrinho com oferta especial"
  5. IVR para Suporte: "Criar menu telefônico: Pressione 1 para vendas, 2 para suporte..."

Quando NÃO usar TWILIO CALL MAKE

  • Informações Simples: Use SMS - é mais barato e menos intrusivo
  • Conversa Complexa: Use atendimento humano - TwiML tem limitações
  • Horário Inadequado: Não ligue à noite/madrugada - respeite horários comerciais
  • Sem TwiML preparado: Este node exige URL com XML TwiML válido

Parâmetros Detalhados

from (string, obrigatório)

O que é: Número de telefone Twilio de origem (comprado na sua conta Twilio) usado para fazer a chamada.

Formato: +5511999887766 (formato E.164 com código do país)

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Twilio Call - From Number",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Realizar Chamada",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "responseVariable": "callResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Confirmar Chamada",
        "parameters": {
          "message": "Chamada iniciada!\n\nCall SID: {{callResult.callSid}}\nDe: {{callResult.from}}\nPara: {{callResult.to}}\nStatus: {{callResult.status}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Configure twilioAccountSid e twilioAuthToken nas variáveis 2. Substitua from por número Twilio válido da sua conta 3. Substitua to pelo seu número de celular 4. Substitua twimlUrl por URL TwiML válida (pode criar em Twilio Console > TwiML Bins) 5. Execute o flow e receba a chamada


to (string, obrigatório)

O que é: Número de telefone do destinatário que receberá a chamada automática.

Formato: +5511988776655 (formato E.164: + código país + DDD + número)

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Twilio Call - To Number Dynamic",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Solicitar Número",
        "parameters": {
          "message": "Digite o número para ligar (com código do país, ex: +5511999887766):",
          "variable": "phoneNumber"
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Realizar Chamada",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "{{phoneNumber}}",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "responseVariable": "callResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Resultado",
        "parameters": {
          "message": "Ligando para {{callResult.to}}...\n\nCall SID: {{callResult.callSid}}\nStatus: {{callResult.status}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Execute o flow 2. Digite um número válido quando solicitado 3. A chamada será iniciada para o número digitado 4. Telefone tocará com áudio do TwiML configurado


twimlUrl (string, obrigatório)

O que é: URL pública que retorna XML TwiML com instruções do que fazer durante a chamada (falar, gravar, coletar dígitos, tocar música, etc).

Formato: URL HTTPS válida que retorna Content-Type: text/xml

Exemplo de TwiML:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice" language="pt-BR">Olá! Esta é uma chamada automática do Lumina Flow.</Say>
    <Pause length="1"/>
    <Say voice="alice" language="pt-BR">Para confirmar, pressione 1. Para cancelar, pressione 2.</Say>
    <Gather numDigits="1" action="/handle-response"/>
</Response>

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Twilio Call - TwiML URL",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Configurar TwiML",
        "parameters": {
          "variables": {
            "customerName": "Maria Silva",
            "appointmentDate": "22 de janeiro",
            "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef"
          }
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Ligar",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "{{twimlUrl}}",
        "responseVariable": "callResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Chamada iniciada com TwiML!\n\nCall SID: {{callResult.callSid}}\nTwiML: {{twimlUrl}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Crie TwiML Bin no Twilio Console ou hospede XML em servidor 2. Configure a URL no flow 3. Execute e receba chamada com áudio personalizado


statusCallback (string, opcional)

O que é: URL que receberá webhooks do Twilio com atualizações de status da chamada (iniciada, completada, falhada, ocupado, sem resposta, etc).

Formato: URL HTTPS válida que aceita POST com dados da chamada

Status recebidos: - initiated: Chamada iniciada - ringing: Tocando - answered: Atendida - completed: Completada - busy: Ocupado - no-answer: Não atendeu - failed: Falhou - canceled: Cancelada

Padrão: Nenhum (opcional)

Flow completo para testar:

{
  "name": "Teste Twilio Call - Status Callback",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Configurar Webhook",
        "parameters": {
          "variables": {
            "webhookUrl": "https://lumina.app.br/webhooks/twilio-call-status"
          }
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Ligar com Callback",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "statusCallback": "{{webhookUrl}}",
        "responseVariable": "callResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Informar",
        "parameters": {
          "message": "Chamada iniciada!\n\nCall SID: {{callResult.callSid}}\n\nWebhook configurado: {{webhookUrl}}\n\nVocê receberá atualizações de status via POST."
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Configure endpoint para receber POST (ex: webhook.site para testes) 2. Execute o flow 3. Seu endpoint receberá múltiplos POSTs conforme status muda 4. Payload inclui: CallSid, CallStatus, CallDuration, etc.


config.accountSid (string, obrigatório)

O que é: Identificador único da sua conta Twilio. É como o "username" da API Twilio.

Onde encontrar: 1. Acesse https://console.twilio.com 2. No Dashboard, procure "Account SID" 3. Formato: ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (34 caracteres começando com "AC")

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Twilio Call - Account SID",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Solicitar Account SID",
        "parameters": {
          "message": "Cole seu Twilio Account SID:",
          "variable": "twilioAccountSid"
        }
      }
    },
    {
      "id": "input_2",
      "type": "input",
      "position": { "x": 300, "y": 200 },
      "data": {
        "label": "Solicitar Auth Token",
        "parameters": {
          "message": "Cole seu Twilio Auth Token:",
          "variable": "twilioAuthToken"
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 150 },
      "data": {
        "label": "Testar Credenciais",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "responseVariable": "testResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 150 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "Credenciais válidas!\n\nChamada iniciada com Call SID: {{testResult.callSid}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 150 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "input_2" },
    { "source": "input_2", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Execute o flow 2. Cole seu Account SID 3. Cole seu Auth Token 4. Se válido: Chamada iniciada 5. Se inválido: Erro de autenticação


config.authToken (string, obrigatório)

O que é: Token de autenticação secreto da sua conta Twilio. É como a "senha" da API Twilio.

Onde encontrar: 1. Acesse https://console.twilio.com 2. No Dashboard, procure "Auth Token" 3. Clique no ícone de olho para revelar 4. Formato: 32 caracteres hexadecimais

Segurança: - NUNCA compartilhe seu Auth Token - NUNCA commit em repositórios públicos - Use variáveis de ambiente ou cofre de senhas

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Twilio Call - Segurança Auth Token",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Carregar Credenciais Seguras",
        "parameters": {
          "variables": {
            "twilioAccountSid": "{{env.TWILIO_ACCOUNT_SID}}",
            "twilioAuthToken": "{{env.TWILIO_AUTH_TOKEN}}"
          }
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Chamada Segura",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "responseVariable": "callResult",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Confirmação",
        "parameters": {
          "message": "Chamada iniciada com credenciais seguras!\n\nCall SID: {{callResult.callSid}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: 1. Configure variáveis de ambiente 2. Execute o flow 3. Credenciais carregadas de forma segura 4. Chamada iniciada sem expor tokens


responseVariable (string, opcional)

O que é: Nome da variável onde será armazenada a resposta da API Twilio com detalhes da chamada iniciada.

Dados retornados: - callSid: ID único da chamada (formato: CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) - status: Status atual (queued, ringing, in-progress, completed, failed) - to: Número do destinatário - from: Número de origem

Padrão: Nenhum (opcional)

Flow completo para testar:

{
  "name": "Teste Twilio Call - Response Variable",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Iniciar Chamada",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "+5511988776655",
        "twimlUrl": "https://handler.twilio.com/twiml/EH1234567890abcdef",
        "responseVariable": "twilioCallResponse",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Exibir Detalhes",
        "parameters": {
          "message": "Chamada Iniciada!\n\n🆔 Call SID: {{twilioCallResponse.callSid}}\n📊 Status: {{twilioCallResponse.status}}\n📤 De: {{twilioCallResponse.from}}\n📥 Para: {{twilioCallResponse.to}}"
        }
      }
    },
    {
      "id": "condition_1",
      "type": "condition",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Verificar Status",
        "parameters": {
          "condition": "{{twilioCallResponse.status}} == 'queued' || {{twilioCallResponse.status}} == 'ringing'"
        }
      }
    },
    {
      "id": "message_success",
      "type": "message",
      "position": { "x": 900, "y": 50 },
      "data": {
        "label": "Sucesso",
        "parameters": {
          "message": "Chamada na fila ou tocando! Aguarde atendimento."
        }
      }
    },
    {
      "id": "message_error",
      "type": "message",
      "position": { "x": 900, "y": 150 },
      "data": {
        "label": "Problema",
        "parameters": {
          "message": "Problema ao iniciar chamada. Status: {{twilioCallResponse.status}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "condition_1" },
    { "source": "condition_1", "target": "message_success", "label": "true" },
    { "source": "condition_1", "target": "message_error", "label": "false" },
    { "source": "message_success", "target": "end_1" },
    { "source": "message_error", "target": "end_1" }
  ]
}

Teste: 1. Execute o flow 2. Veja todos os detalhes da chamada 3. Flow verifica status e mostra mensagem apropriada 4. Use twilioCallResponse.callSid para rastrear chamada posteriormente

Parâmetros

Campo Tipo Obrigatório Descrição
resource string Sim Deve ser "call"
operation string Sim Deve ser "make"
from string Sim Número Twilio de origem (formato E.164)
to string Sim Número do destinatário (formato E.164)
twimlUrl string Sim URL pública que retorna XML TwiML
statusCallback string Não URL para receber webhooks de status da chamada
config.accountSid string Sim Account SID da sua conta Twilio
config.authToken string Sim Auth Token secreto da sua conta Twilio
responseVariable string Não Nome da variável para armazenar resposta

Exemplo 1: IVR Menu Interativo

Objetivo: Criar menu telefônico com opções por DTMF (teclado numérico)

JSON para Importar

{
  "name": "IVR Menu - Atendimento Automático",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Configurar TwiML Menu",
        "parameters": {
          "variables": {
            "twimlMenuUrl": "https://handler.twilio.com/twiml/EH_MENU_PRINCIPAL",
            "webhookCallback": "https://lumina.app.br/webhooks/ivr-response"
          }
        }
      }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Número do Cliente",
        "parameters": {
          "message": "Digite o número do cliente para ligar:",
          "variable": "customerPhone"
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Iniciar IVR",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "{{customerPhone}}",
        "twimlUrl": "{{twimlMenuUrl}}",
        "statusCallback": "{{webhookCallback}}",
        "responseVariable": "ivrCall",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "IVR Ativo",
        "parameters": {
          "message": "IVR ativo para {{customerPhone}}!\n\nCall SID: {{ivrCall.callSid}}\n\nMenu:\n1 - Vendas\n2 - Suporte\n3 - Financeiro\n0 - Falar com atendente"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "input_1" },
    { "source": "input_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

TwiML do Menu (hospede em Twilio TwiML Bin):

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice" language="pt-BR">Bem-vindo à Lumina App. Escolha uma opção.</Say>
    <Gather numDigits="1" action="https://lumina.app.br/webhooks/ivr-option" method="POST">
        <Say voice="alice" language="pt-BR">Para vendas, pressione 1.</Say>
        <Say voice="alice" language="pt-BR">Para suporte técnico, pressione 2.</Say>
        <Say voice="alice" language="pt-BR">Para financeiro, pressione 3.</Say>
        <Say voice="alice" language="pt-BR">Para falar com atendente, pressione 0.</Say>
    </Gather>
    <Say voice="alice" language="pt-BR">Nenhuma opção foi selecionada. Encerrando chamada.</Say>
    <Hangup/>
</Response>

Saída esperada:

Sistema: Digite o número do cliente para ligar:
Usuário: +5511988776655
Sistema: IVR ativo para +5511988776655!
Call SID: CA1234567890abcdef
Menu:
1 - Vendas
2 - Suporte
3 - Financeiro
0 - Falar com atendente

[Telefone toca]
[Cliente atende e ouve: "Bem-vindo à Lumina App. Escolha uma opção..."]
[Cliente pressiona 2]
[Sistema roteia para suporte baseado no webhook]

Exemplo 2: Confirmação de Consulta Médica

Objetivo: Ligar para paciente e coletar confirmação de consulta por voz

JSON para Importar

{
  "name": "Confirmação de Consulta - Chamada Automática",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados da Consulta",
        "parameters": {
          "variables": {
            "patientName": "João Silva",
            "patientPhone": "+5511988776655",
            "doctorName": "Dra. Maria Santos",
            "appointmentDate": "22 de janeiro de 2025",
            "appointmentTime": "14:30",
            "twimlConfirmUrl": "https://handler.twilio.com/twiml/EH_CONFIRM_APPOINTMENT"
          }
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Ligar para Confirmar",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "{{patientPhone}}",
        "twimlUrl": "{{twimlConfirmUrl}}",
        "statusCallback": "https://lumina.app.br/webhooks/appointment-status",
        "responseVariable": "confirmCall",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Chamada Iniciada",
        "parameters": {
          "message": "Ligando para {{patientName}}...\n\nConsulta: {{doctorName}}\nData: {{appointmentDate}} às {{appointmentTime}}\n\nCall SID: {{confirmCall.callSid}}"
        }
      }
    },
    {
      "id": "delay_1",
      "type": "delay",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Aguardar Resposta",
        "parameters": {
          "delay": 30000
        }
      }
    },
    {
      "id": "message_2",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Aguardando",
        "parameters": {
          "message": "Aguardando resposta do paciente...\n\nVerifique o webhook para saber se confirmou ou cancelou."
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "delay_1" },
    { "source": "delay_1", "target": "message_2" },
    { "source": "message_2", "target": "end_1" }
  ]
}

TwiML de Confirmação:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice" language="pt-BR">Olá, João Silva. Esta é uma chamada da Clínica Lumina.</Say>
    <Pause length="1"/>
    <Say voice="alice" language="pt-BR">Você tem consulta marcada com Doutora Maria Santos para o dia 22 de janeiro de 2025, às 14 horas e 30 minutos.</Say>
    <Pause length="1"/>
    <Gather numDigits="1" action="https://lumina.app.br/webhooks/confirm-appointment" method="POST">
        <Say voice="alice" language="pt-BR">Para confirmar sua presença, pressione 1.</Say>
        <Say voice="alice" language="pt-BR">Para cancelar, pressione 2.</Say>
        <Say voice="alice" language="pt-BR">Para reagendar, pressione 3.</Say>
    </Gather>
    <Say voice="alice" language="pt-BR">Não recebemos sua confirmação. Ligaremos novamente. Obrigado.</Say>
    <Hangup/>
</Response>

Saída esperada:

Sistema: Ligando para João Silva...
Consulta: Dra. Maria Santos
Data: 22 de janeiro de 2025 às 14:30
Call SID: CA9876543210fedcba

[Telefone toca]
[Paciente atende e ouve mensagem]
[Paciente pressiona 1 para confirmar]
[Webhook recebe confirmação]

Sistema: Aguardando resposta do paciente...
Verifique o webhook para saber se confirmou ou cancelou.

[Webhook registra: "Paciente confirmou - Digits: 1"]

Exemplo 3: Pesquisa de Satisfação Pós-Compra

Objetivo: Ligar para coletar NPS (Net Promoter Score) após compra

JSON para Importar

{
  "name": "Pesquisa NPS - Chamada Automática",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Dados do Cliente",
        "parameters": {
          "variables": {
            "customerName": "Ana Costa",
            "customerPhone": "+5511988776655",
            "orderNumber": "ORD-54321",
            "productName": "Notebook Lumina Pro",
            "purchaseDate": "10/01/2025",
            "twimlNpsUrl": "https://handler.twilio.com/twiml/EH_NPS_SURVEY"
          }
        }
      }
    },
    {
      "id": "twilio_1",
      "type": "twilio",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Iniciar Pesquisa",
        "resource": "call",
        "operation": "make",
        "from": "+5511999887766",
        "to": "{{customerPhone}}",
        "twimlUrl": "{{twimlNpsUrl}}",
        "statusCallback": "https://lumina.app.br/webhooks/nps-response",
        "responseVariable": "npsCall",
        "config": {
          "accountSid": "{{twilioAccountSid}}",
          "authToken": "{{twilioAuthToken}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Pesquisa Iniciada",
        "parameters": {
          "message": "Pesquisa NPS iniciada!\n\nCliente: {{customerName}}\nProduto: {{productName}}\nPedido: {{orderNumber}}\n\nCall SID: {{npsCall.callSid}}"
        }
      }
    },
    {
      "id": "delay_1",
      "type": "delay",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Aguardar Pesquisa",
        "parameters": {
          "delay": 45000
        }
      }
    },
    {
      "id": "message_2",
      "type": "message",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Finalizado",
        "parameters": {
          "message": "Pesquisa concluída!\n\nVerifique o webhook para ver a nota dada pelo cliente (0-10)."
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1300, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "twilio_1" },
    { "source": "twilio_1", "target": "message_1" },
    { "source": "message_1", "target": "delay_1" },
    { "source": "delay_1", "target": "message_2" },
    { "source": "message_2", "target": "end_1" }
  ]
}

TwiML da Pesquisa NPS:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice" language="pt-BR">Olá, Ana Costa. Aqui é da Lumina. Obrigado por comprar o Notebook Lumina Pro.</Say>
    <Pause length="1"/>
    <Say voice="alice" language="pt-BR">Gostaríamos de saber: em uma escala de 0 a 10, quanto você recomendaria nossa empresa para um amigo ou colega?</Say>
    <Pause length="1"/>
    <Gather numDigits="2" action="https://lumina.app.br/webhooks/nps-score" method="POST" timeout="10">
        <Say voice="alice" language="pt-BR">Digite sua nota de 0 a 10 no teclado e pressione sustenido.</Say>
    </Gather>
    <Say voice="alice" language="pt-BR">Não recebemos sua nota. Muito obrigado mesmo assim!</Say>
    <Hangup/>
</Response>

Saída esperada:

Sistema: Pesquisa NPS iniciada!
Cliente: Ana Costa
Produto: Notebook Lumina Pro
Pedido: ORD-54321
Call SID: CA1122334455667788

[Telefone toca]
[Cliente atende]
[Ouve: "Olá, Ana Costa... quanto você recomendaria nossa empresa..."]
[Cliente digita: 9#]
[Webhook recebe: score=9, detractor/passive/promoter=promoter]

Sistema: Pesquisa concluída!
Verifique o webhook para ver a nota dada pelo cliente (0-10).

[Analytics registra: NPS Score = 9 (Promoter)]

Resposta do Node

{
  "callSid": "CA1234567890abcdef1234567890abcdef",
  "status": "queued",
  "to": "+5511988776655",
  "from": "+5511999887766"
}

Status possíveis: - queued: Na fila para discar - ringing: Tocando no destinatário - in-progress: Em andamento (atendida) - completed: Chamada completada - busy: Ocupado - no-answer: Não atendeu - failed: Falhou - canceled: Cancelada

Boas Práticas

SIM:

  • Sempre teste TwiML antes de usar em produção (Twilio Console > TwiML Bins)
  • Use statusCallback para rastrear chamadas (atendeu? não atendeu? duração?)
  • Respeite horários comerciais (9h-18h) - não ligue de madrugada
  • Sempre identifique sua empresa no início da chamada
  • Use voz alice com language="pt-BR" para português natural
  • Implemente timeout em <Gather> para não deixar chamada travada
  • Adicione sempre <Hangup/> no final do TwiML para encerrar chamada
  • Configure DNC (Do Not Call) list - não ligue para quem pediu para não receber

NÃO:

  • Nunca ligue para números sem consentimento (LGPD/GDPR)
  • Não use TwiML mal formatado (XML inválido causa chamada muda)
  • Não confie apenas no status inicial queued - use webhook para status real
  • Não teste em massa - faça ligação de teste primeiro
  • Não use vozes ruins - alice é muito superior a woman/man
  • Não crie IVR com mais de 4 opções - usuário não lembra de todas

Dicas

Dica 1: Para TwiML dinâmico (personalizado por cliente), use servidor próprio que gera XML baseado em parâmetros da URL.

Dica 2: Use <Record> no TwiML para gravar respostas do cliente (ex: pesquisa qualitativa).

Dica 3: Para chamadas em massa, use Twilio Bulk API em vez de loop de call:make.

Dica 4: Configure <Dial> com timeout="20" para limitar tempo de espera antes de desistir.

Dica 5: Para melhor conversão, ligue 2-3 vezes em horários diferentes antes de desistir (implemente retry logic).

Dica 6: Use <Say> com SSML (Speech Synthesis Markup Language) para controlar velocidade, tom e pausas da voz.

Dica 7: Em produção, implemente fallback para atendente humano se cliente ficar confuso no IVR.

Próximo Node

TWILIO SMS SEND - Enviar SMS/MMS via Twilio → WEBHOOK - Receber callbacks de status → CONDITION - Verificar status da chamada → DELAY - Aguardar entre tentativas de chamada