GOOGLE_CALENDAR - Integração com Google Calendar
O que é este Node?
O GOOGLE_CALENDAR é o node responsável por gerenciar eventos no Google Calendar, permitindo criar, listar e deletar compromissos automaticamente via flow.
Por que este Node existe?
Agendamentos precisam ser gerenciados automaticamente. O GOOGLE_CALENDAR existe para:
- Agendar automaticamente: Criar eventos sem intervenção manual
- Enviar convites: Adicionar participantes e enviar notificações
- Consultar agenda: Verificar disponibilidade e eventos futuros
- Integrar sistemas: Sincronizar agendamentos entre plataformas
- Lembrar compromissos: Garantir que eventos sejam registrados
Como funciona internamente?
Quando o GOOGLE_CALENDAR é executado, o sistema:
- Autentica via OAuth2 com Google (access_token e refresh_token)
- Conecta à API do Google Calendar v3
- Executa operação solicitada (createEvent, listEvents, deleteEvent)
- Processa datas em formato ISO 8601 (2025-01-15T10:30:00)
- Gerencia participantes automaticamente com envio de emails
- Retorna evento criado com ID e link de visualização
- Se erro: Retorna mensagem de erro detalhada
Código interno (google-executors.service.ts:353-421):
async executeGoogleCalendar(data: any, variables: Record<string, any>): Promise<any> {
try {
this.logger.log('📅 [GOOGLE CALENDAR] Executing operation');
const oauth2Client = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI
);
oauth2Client.setCredentials({
access_token: data.accessToken,
refresh_token: data.refreshToken,
});
const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
switch (data.operation) {
case 'createEvent':
const createResult = await calendar.events.insert({
calendarId: data.calendarId || 'primary',
requestBody: {
summary: data.summary,
description: data.description,
start: { dateTime: data.startDateTime, timeZone: data.timeZone || 'America/Sao_Paulo' },
end: { dateTime: data.endDateTime, timeZone: data.timeZone || 'America/Sao_Paulo' },
attendees: data.attendees?.map((email: string) => ({ email })) || [],
},
});
return {
success: true,
eventId: createResult.data.id,
htmlLink: createResult.data.htmlLink,
};
case 'listEvents':
const listResult = await calendar.events.list({
calendarId: data.calendarId || 'primary',
timeMin: data.timeMin || new Date().toISOString(),
maxResults: data.maxResults || 10,
singleEvents: true,
orderBy: 'startTime',
});
return {
success: true,
events: listResult.data.items || [],
};
case 'deleteEvent':
await calendar.events.delete({
calendarId: data.calendarId || 'primary',
eventId: data.eventId,
});
return {
success: true,
message: 'Event deleted successfully',
};
default:
throw new Error(`Unknown Google Calendar operation: ${data.operation}`);
}
} catch (error) {
this.logger.error('Google Calendar execution error:', error);
throw error;
}
}
Quando você DEVE usar este Node?
Use GOOGLE_CALENDAR sempre que precisar gerenciar eventos automaticamente:
Casos de uso
- Agendar consultas: "Criar evento para consulta médica às 14h"
- Reuniões automáticas: "Agendar reunião com time de vendas"
- Lembretes: "Criar lembrete de follow-up com cliente"
- Integração CRM: "Sincronizar agendamentos do sistema"
- Consultar disponibilidade: "Verificar próximos compromissos"
- Cancelar eventos: "Deletar reunião cancelada"
Quando NÃO usar GOOGLE_CALENDAR
- Agendamentos internos simples: Use sistema próprio se não precisa do Google
- Eventos sem data/hora: Calendar requer datetime específico
- Alta frequência: Considere rate limits da API
Parâmetros
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| operation | string | Sim | createEvent, listEvents, deleteEvent |
| summary | string | Condicional | Título do evento (para createEvent) |
| description | string | Não | Descrição detalhada do evento |
| startDateTime | string | Condicional | Data/hora início (ISO 8601) |
| endDateTime | string | Condicional | Data/hora fim (ISO 8601) |
| timeZone | string | Não | Timezone (padrão: America/Sao_Paulo) |
| attendees | array | Não | Lista de emails dos participantes |
| calendarId | string | Não | ID do calendar (padrão: primary) |
| eventId | string | Condicional | ID do evento (para deleteEvent) |
| timeMin | string | Não | Data mínima para listEvents |
| maxResults | number | Não | Máximo de eventos (padrão: 10) |
| accessToken | string | Sim | Token OAuth2 do Google |
| refreshToken | string | Sim | Refresh token OAuth2 do Google |
Operações Disponíveis
1. createEvent - Criar evento
O que faz: Cria novo evento no Google Calendar.
Parâmetros obrigatórios: - summary - startDateTime - endDateTime - accessToken - refreshToken
Parâmetros opcionais: - description - attendees - timeZone - calendarId
2. listEvents - Listar eventos
O que faz: Lista próximos eventos do calendar.
Parâmetros obrigatórios: - accessToken - refreshToken
Parâmetros opcionais: - calendarId - timeMin (data inicial) - maxResults
3. deleteEvent - Deletar evento
O que faz: Remove evento do calendar.
Parâmetros obrigatórios: - eventId - accessToken - refreshToken
Parâmetros opcionais: - calendarId
Exemplo 1: Agendar Consulta Médica
Objetivo: Criar evento de consulta com horário específico.
JSON para Importar
{
"name": "Agendar Consulta no Google Calendar",
"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": "Nome Paciente",
"parameters": {
"message": "Nome do paciente:",
"variable": "paciente_nome"
}
}
},
{
"id": "email_1",
"type": "email",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Email Paciente",
"parameters": {
"message": "Email do paciente:",
"variable": "paciente_email"
}
}
},
{
"id": "input_2",
"type": "input",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Data e Hora",
"parameters": {
"message": "Data/hora da consulta (Ex: 2025-01-20 14:00):",
"variable": "data_hora"
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Calcular Fim",
"parameters": {
"operation": "set",
"name": "data_hora_fim",
"value": "{{data_hora}} + 1 hour"
}
}
},
{
"id": "google_calendar_1",
"type": "google_calendar",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Criar Evento",
"parameters": {
"operation": "createEvent",
"summary": "Consulta - {{paciente_nome}}",
"description": "Consulta médica agendada",
"startDateTime": "{{data_hora}}",
"endDateTime": "{{data_hora_fim}}",
"timeZone": "America/Sao_Paulo",
"attendees": ["{{paciente_email}}"],
"accessToken": "{{google_access_token}}",
"refreshToken": "{{google_refresh_token}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 1300, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "✅ Consulta agendada!\n\n👤 Paciente: {{paciente_nome}}\n📅 Data/Hora: {{data_hora}}\n📧 Convite enviado para: {{paciente_email}}\n\n🔗 Ver no Calendar: {{htmlLink}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1500, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "email_1" },
{ "source": "email_1", "target": "input_2" },
{ "source": "input_2", "target": "variable_1" },
{ "source": "variable_1", "target": "google_calendar_1" },
{ "source": "google_calendar_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Saída esperada:
Sistema: Nome do paciente:
Usuário: João Silva
Sistema: Email do paciente:
Usuário: joao@example.com
Sistema: Data/hora da consulta (Ex: 2025-01-20 14:00):
Usuário: 2025-01-20T14:00:00
Sistema: ✅ Consulta agendada!
👤 Paciente: João Silva
📅 Data/Hora: 2025-01-20T14:00:00
📧 Convite enviado para: joao@example.com
🔗 Ver no Calendar: https://calendar.google.com/event?eid=...
Exemplo 2: Listar Próximos Compromissos
Objetivo: Mostrar próximos 5 eventos do calendar.
JSON para Importar
{
"name": "Listar Compromissos do Google Calendar",
"nodes": [
{
"id": "start_1",
"type": "start",
"position": { "x": 100, "y": 100 },
"data": { "label": "Início" }
},
{
"id": "google_calendar_1",
"type": "google_calendar",
"position": { "x": 300, "y": 100 },
"data": {
"label": "Buscar Eventos",
"parameters": {
"operation": "listEvents",
"maxResults": 5,
"accessToken": "{{google_access_token}}",
"refreshToken": "{{google_refresh_token}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Mostrar Lista",
"parameters": {
"message": "📅 Seus próximos compromissos:\n\n(Lista processada dos eventos)"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 700, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "google_calendar_1" },
{ "source": "google_calendar_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Exemplo 3: Criar Reunião com Equipe
Objetivo: Agendar reunião e enviar convites para múltiplos participantes.
JSON para Importar
{
"name": "Agendar Reunião de Equipe",
"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": "Título",
"parameters": {
"message": "Título da reunião:",
"variable": "titulo"
}
}
},
{
"id": "input_2",
"type": "input",
"position": { "x": 500, "y": 100 },
"data": {
"label": "Descrição",
"parameters": {
"message": "Pauta da reunião:",
"variable": "pauta"
}
}
},
{
"id": "variable_1",
"type": "variable",
"position": { "x": 700, "y": 100 },
"data": {
"label": "Definir Participantes",
"parameters": {
"operation": "set",
"name": "participantes",
"value": ["maria@empresa.com", "joao@empresa.com", "ana@empresa.com"]
}
}
},
{
"id": "google_calendar_1",
"type": "google_calendar",
"position": { "x": 900, "y": 100 },
"data": {
"label": "Criar Reunião",
"parameters": {
"operation": "createEvent",
"summary": "{{titulo}}",
"description": "{{pauta}}",
"startDateTime": "2025-01-25T10:00:00",
"endDateTime": "2025-01-25T11:00:00",
"timeZone": "America/Sao_Paulo",
"attendees": "{{participantes}}",
"accessToken": "{{google_access_token}}",
"refreshToken": "{{google_refresh_token}}"
}
}
},
{
"id": "message_1",
"type": "message",
"position": { "x": 1100, "y": 100 },
"data": {
"label": "Confirmar",
"parameters": {
"message": "✅ Reunião criada!\n\n📋 {{titulo}}\n👥 3 participantes convidados\n📅 25/01/2025 às 10:00\n\n🔗 {{htmlLink}}"
}
}
},
{
"id": "end_1",
"type": "end",
"position": { "x": 1300, "y": 100 },
"data": { "label": "Fim" }
}
],
"edges": [
{ "source": "start_1", "target": "input_1" },
{ "source": "input_1", "target": "input_2" },
{ "source": "input_2", "target": "variable_1" },
{ "source": "variable_1", "target": "google_calendar_1" },
{ "source": "google_calendar_1", "target": "message_1" },
{ "source": "message_1", "target": "end_1" }
]
}
Resposta do Node
Operação createEvent
{
"success": true,
"eventId": "abc123xyz...",
"htmlLink": "https://www.google.com/calendar/event?eid=..."
}
Operação listEvents
{
"success": true,
"events": [
{
"id": "event123",
"summary": "Reunião com Cliente",
"start": {
"dateTime": "2025-01-20T14:00:00-03:00"
},
"end": {
"dateTime": "2025-01-20T15:00:00-03:00"
},
"htmlLink": "https://www.google.com/calendar/event?eid=..."
}
]
}
Operação deleteEvent
{
"success": true,
"message": "Event deleted successfully"
}
Formato de Data/Hora (ISO 8601)
O Google Calendar requer datas no formato ISO 8601:
2025-01-20T14:00:00 (14h do dia 20/01/2025)
2025-01-20T14:00:00-03:00 (com timezone explícito)
Exemplos práticos:
| Descrição | Formato |
|---|---|
| Hoje 14h | 2025-01-15T14:00:00 |
| Amanhã 9h | 2025-01-16T09:00:00 |
| Próxima semana 10h30 | 2025-01-22T10:30:00 |
Timezones Comuns
| Timezone | Descrição |
|---|---|
| America/Sao_Paulo | Brasília (UTC-3) |
| America/New_York | Nova York (UTC-5/4) |
| Europe/London | Londres (UTC+0/1) |
| Asia/Tokyo | Tóquio (UTC+9) |
Como obter eventId
O eventId é retornado ao criar o evento e também está na URL:
https://calendar.google.com/event?eid=abc123xyz...
^^^^^^^^^^^
eventId (codificado)
Use a API para obter o ID real ou salve-o ao criar o evento.
Participantes (Attendees)
O parâmetro attendees aceita array de emails:
["maria@example.com", "joao@example.com"]
Cada participante receberá: - Email de convite automático - Opções de Aceitar/Recusar - Notificações de alterações
Boas Práticas
✅ SIM:
- Sempre especifique timezone para evitar confusão
- Use descrição clara do evento
- Adicione emails válidos em attendees
- Salve eventId para operações futuras
- Configure lembretes via API se necessário
❌ NÃO:
- Não crie eventos sem validar datas
- Não envie convites para emails inválidos
- Não delete eventos sem confirmação
- Não ignore timezone (pode causar horário errado)
- Não crie eventos duplicados
Dicas
💡 Convites automáticos: attendees recebem email automaticamente
💡 Duração padrão: Use 1 hora de duração se não especificado
💡 Eventos recorrentes: Use regras RRULE para eventos que se repetem
💡 Cores: Configure cores diferentes para categorias de eventos
💡 Links úteis: htmlLink permite abrir diretamente no Google Calendar
Troubleshooting
Erro: "Invalid dateTime value"
Causa: Formato de data incorreto
Solução: Use formato ISO 8601: 2025-01-20T14:00:00
Erro: "Event not found"
Causa: eventId inválido ou evento já deletado
Solução: Liste eventos para obter IDs válidos
Erro: "Insufficient permissions"
Causa: Token sem scope de calendar
Solução: Adicione scope https://www.googleapis.com/auth/calendar
Erro: "Invalid attendee email"
Causa: Email inválido em attendees
Solução: Valide emails antes de adicionar como participantes
Próximo Node
→ GOOGLE_SHEETS - Integrar com Google Sheets → GOOGLE_DRIVE - Gerenciar arquivos no Drive → GOOGLE_DOCS - Criar e editar documentos