This workflow follows the Informationextractor → Google Gemini Chat recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Instituto Ariana Borges - Agendamento",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "get_availability",
"responseMode": "responseNode",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
600
],
"id": "webhook-get-availability",
"name": "Webhook - Verificar Disponibilidade"
},
{
"parameters": {
"httpMethod": "POST",
"path": "book_appointment",
"responseMode": "responseNode",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
240,
300
],
"id": "webhook-book-appointment",
"name": "Webhook - Agendar"
},
{
"parameters": {
"jsCode": "const customerName = $input.item.json.customer_name;\nconst customerPhone = $input.item.json.customer_phone;\nconst customerEmail = $input.item.json.customer_email;\nconst serviceType = $input.item.json.service_type;\nconst description = $input.item.json.description;\nconst preferredTimeframe = $input.item.json.preferred_timeframe;\n\nreturn {\n json: {\n customerName,\n customerPhone,\n customerEmail,\n serviceType,\n description,\n preferredTimeframe,\n currentDate: new Date().toISOString()\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
],
"id": "extract-booking-data",
"name": "Extrair Dados do Booking"
},
{
"parameters": {
"jsCode": "const preferredTimeframe = $input.item.json.preferred_timeframe;\nconst currentDate = new Date().toISOString();\n\nreturn {\n json: {\n preferredTimeframe,\n currentDate\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
600
],
"id": "extract-availability-data",
"name": "Extrair Dados de Disponibilidade"
},
{
"parameters": {
"text": "={{ $json.preferredTimeframe }}",
"schemaType": "fromJson",
"jsonSchemaExample": "{\n \"startDateTime\": \"YYYY-MM-DDTHH:mm:00-03:00\",\n \"endDateTime\": \"YYYY-MM-DDTHH:mm:00-03:00\",\n \"interpretedDate\": \"descri\u00e7\u00e3o leg\u00edvel\"\n}",
"options": {
"systemPromptTemplate": "Voc\u00ea \u00e9 um assistente que converte texto em datas no formato ISO 8601.\n\nDATA ATUAL DE REFER\u00caNCIA: {{$json.currentDate}}\nTIMEZONE: America/Sao_Paulo (UTC-03:00)\n\nSua tarefa:\n1. Interpretar o texto de hor\u00e1rio preferido\n2. Converter para formato ISO 8601 com timezone -03:00\n3. Dura\u00e7\u00e3o padr\u00e3o: 1 hora\n4. Hor\u00e1rio comercial: 9h \u00e0s 18h\n5. Se for \"manh\u00e3\": 9h, \"tarde\": 14h, \"qualquer hor\u00e1rio\": 14h\n\nFormato de resposta (JSON):\n- startDateTime: string ISO 8601 (exemplo: 2026-01-30T14:00:00-03:00)\n- endDateTime: string ISO 8601 (1 hora depois do start)\n- interpretedDate: string leg\u00edvel (exemplo: Ter\u00e7a-feira, 30 de janeiro \u00e0s 14h)\n\nRetorne APENAS o JSON sem markdown."
}
},
"type": "@n8n/n8n-nodes-langchain.informationExtractor",
"typeVersion": 1,
"position": [
680,
300
],
"id": "ai-parse-date-booking",
"name": "IA - Converter Texto para Data"
},
{
"parameters": {
"text": "={{ $json.preferredTimeframe }}",
"schemaType": "fromJson",
"jsonSchemaExample": "{\n \"available\": true,\n \"requestedDateTime\": \"YYYY-MM-DDTHH:mm:00-03:00\",\n \"message\": \"mensagem amig\u00e1vel sobre disponibilidade\",\n \"suggestedTimes\": [\"hor\u00e1rio1\", \"hor\u00e1rio2\", \"hor\u00e1rio3\"]\n}",
"options": {
"systemPromptTemplate": "Voc\u00ea \u00e9 um assistente que verifica disponibilidade de agenda.\n\nDATA ATUAL: {{$json.currentDate}}\nTIMEZONE: America/Sao_Paulo\nHOR\u00c1RIO COMERCIAL: 9h \u00e0s 18h (Segunda a Sexta)\n\nSua tarefa:\n1. Interpretar o hor\u00e1rio solicitado\n2. Converter para data ISO 8601 com timezone -03:00\n3. Verificar se est\u00e1 dentro do hor\u00e1rio comercial\n4. OBRIGAT\u00d3RIO: Sugerir SEMPRE pelo menos 3 hor\u00e1rios alternativos dispon\u00edveis\n\nFormato de resposta (JSON):\n- available: boolean (true ou false)\n- requestedDateTime: string ISO 8601 (exemplo: 2026-01-30T14:00:00-03:00)\n- message: string com mensagem amig\u00e1vel sobre a disponibilidade\n- suggestedTimes: array de strings - OBRIGAT\u00d3RIO incluir pelo menos 3 hor\u00e1rios (exemplos: Segunda-feira \u00e0s 9h, Ter\u00e7a-feira \u00e0s 14h, Quarta-feira \u00e0s 10h)\n\nIMPORTANTE: O campo suggestedTimes NUNCA pode estar vazio. Sempre sugira hor\u00e1rios pr\u00f3ximos ao solicitado.\n\nRetorne APENAS o JSON sem markdown."
}
},
"type": "@n8n/n8n-nodes-langchain.informationExtractor",
"typeVersion": 1,
"position": [
680,
600
],
"id": "ai-check-availability",
"name": "IA - Verificar Disponibilidade"
},
{
"parameters": {
"jsCode": "const aiResponse = $input.item.json.output;\n\nlet parsedDate;\ntry {\n // Check if aiResponse is already an object or a string\n if (typeof aiResponse === 'object') {\n parsedDate = aiResponse;\n } else if (typeof aiResponse === 'string') {\n const cleanJson = aiResponse.replace(/```json\\n?|```/g, '').trim();\n parsedDate = JSON.parse(cleanJson);\n } else {\n throw new Error('Formato de resposta inesperado');\n }\n} catch (error) {\n throw new Error('Erro ao parsear resposta da IA: ' + error.message);\n}\n\nif (!parsedDate.startDateTime || !parsedDate.endDateTime) {\n throw new Error('IA n\u00e3o retornou datas v\u00e1lidas');\n}\n\nreturn {\n json: {\n customerName: $input.item.json.customerName,\n customerPhone: $input.item.json.customerPhone,\n customerEmail: $input.item.json.customerEmail,\n serviceType: $input.item.json.serviceType,\n description: $input.item.json.description,\n startDateTime: parsedDate.startDateTime,\n endDateTime: parsedDate.endDateTime,\n interpretedDate: parsedDate.interpretedDate,\n eventSummary: `${$input.item.json.serviceType} - ${$input.item.json.customerName}`,\n eventDescription: `\ud83d\udccb Agendamento\\n\\n\ud83d\udc64 Cliente: ${$input.item.json.customerName}\\n\ud83d\udce7 Email: ${$input.item.json.customerEmail}\\n\ud83d\udcf1 Telefone: ${$input.item.json.customerPhone}\\n\\n\ud83c\udf1f Servi\u00e7o: ${$input.item.json.serviceType}\\n\ud83d\udcdd ${$input.item.json.description}`\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
300
],
"id": "process-ai-response-booking",
"name": "Processar Resposta da IA"
},
{
"parameters": {
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list",
"cachedResultName": "Calend\u00e1rio Principal"
},
"start": "={{$json.startDateTime}}",
"end": "={{$json.endDateTime}}",
"options": {
"description": "={{$json.eventDescription}}",
"summary": "={{$json.eventSummary}}",
"attendees": [
"={{$json.customerEmail}}"
],
"sendUpdates": "all",
"timeZone": "America/Sao_Paulo"
}
},
"type": "n8n-nodes-base.googleCalendar",
"typeVersion": 1.3,
"position": [
1120,
300
],
"id": "google-calendar-create",
"name": "Google Calendar - Criar Evento",
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list",
"cachedResultName": "Calend\u00e1rio Principal"
},
"options": {
"timeZone": "America/Sao_Paulo"
}
},
"type": "n8n-nodes-base.googleCalendar",
"typeVersion": 1.3,
"position": [
900,
600
],
"id": "google-calendar-list",
"name": "Google Calendar - Listar Eventos",
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const aiResponse = $input.first().json.output;\nconst preferredTimeframe = $input.all().find(item => item.json.preferredTimeframe)?.json.preferredTimeframe || '';\nlet availabilityCheck;\n\ntry {\n // Check if aiResponse is already an object or a string\n if (typeof aiResponse === 'object') {\n availabilityCheck = aiResponse;\n } else if (typeof aiResponse === 'string') {\n const cleanJson = aiResponse.replace(/```json\\n?|```/g, '').trim();\n availabilityCheck = JSON.parse(cleanJson);\n } else {\n throw new Error('Formato de resposta inesperado');\n }\n} catch (error) {\n availabilityCheck = {\n available: true,\n requestedDateTime: new Date().toISOString(),\n message: \"Estamos verificando a disponibilidade.\",\n suggestedTimes: []\n };\n}\n\n// Formatar o hor\u00e1rio solicitado de forma leg\u00edvel\nlet requestedTimeFormatted = preferredTimeframe;\nif (availabilityCheck.requestedDateTime) {\n try {\n const dt = new Date(availabilityCheck.requestedDateTime);\n const daysOfWeek = ['Domingo', 'Segunda-feira', 'Ter\u00e7a-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'S\u00e1bado'];\n const day = daysOfWeek[dt.getDay()];\n const date = dt.getDate();\n const month = dt.getMonth() + 1;\n const hour = dt.getHours();\n requestedTimeFormatted = `${day}, ${date}/${month} \u00e0s ${hour}h`;\n } catch (e) {\n // Manter o formato original se falhar\n }\n}\n\n// Gerar hor\u00e1rios de fallback se IA n\u00e3o retornou\nif (!availabilityCheck.suggestedTimes || availabilityCheck.suggestedTimes.length === 0) {\n const now = new Date();\n const tomorrow = new Date(now);\n tomorrow.setDate(tomorrow.getDate() + 1);\n \n const dayAfter = new Date(now);\n dayAfter.setDate(dayAfter.getDate() + 2);\n \n const daysOfWeek = ['Domingo', 'Segunda-feira', 'Ter\u00e7a-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'S\u00e1bado'];\n \n availabilityCheck.suggestedTimes = [\n `${daysOfWeek[tomorrow.getDay()]}, ${tomorrow.getDate()}/${tomorrow.getMonth() + 1} \u00e0s 9h`,\n `${daysOfWeek[tomorrow.getDay()]}, ${tomorrow.getDate()}/${tomorrow.getMonth() + 1} \u00e0s 14h`,\n `${daysOfWeek[dayAfter.getDay()]}, ${dayAfter.getDate()}/${dayAfter.getMonth() + 1} \u00e0s 10h`\n ];\n}\n\nconst calendarEvents = $input.all().filter(item => item.json.id).map(item => ({\n start: item.json.start?.dateTime,\n end: item.json.end?.dateTime,\n summary: item.json.summary\n}));\n\nconst requestedDateTime = availabilityCheck.requestedDateTime;\nconst hasConflict = calendarEvents.some(event => {\n if (!event.start || !requestedDateTime) return false;\n return new Date(event.start).toISOString() === new Date(requestedDateTime).toISOString();\n});\n\nlet finalMessage;\nif (hasConflict) {\n finalMessage = `\ud83d\ude14 Infelizmente ${requestedTimeFormatted} j\u00e1 est\u00e1 ocupado.`;\n if (availabilityCheck.suggestedTimes?.length > 0) {\n finalMessage += `\\n\\nHor\u00e1rios dispon\u00edveis:\\n${availabilityCheck.suggestedTimes.map(t => `\u2022 ${t}`).join('\\n')}`;\n }\n} else {\n finalMessage = `\u2705 \u2728 \u00d3tima not\u00edcia! ${requestedTimeFormatted} est\u00e1 dispon\u00edvel!`;\n if (availabilityCheck.suggestedTimes?.length > 0) {\n finalMessage += `\\n\\nOutros hor\u00e1rios dispon\u00edveis:\\n${availabilityCheck.suggestedTimes.map(t => `\u2022 ${t}`).join('\\n')}`;\n }\n}\n\nreturn {\n json: {\n available: !hasConflict,\n message: finalMessage,\n requestedDateTime: requestedDateTime,\n suggestedTimes: availabilityCheck.suggestedTimes || []\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
600
],
"id": "process-availability",
"name": "Processar Disponibilidade"
},
{
"parameters": {
"respondWith": "json",
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
},
"responseBody": "={{ JSON.stringify({ success: true, message: 'Tudo certo! Voc\u00ea est\u00e1 agendado(a) para ' + $('Processar Resposta da IA').item.json.interpretedDate + '. \ud83c\udf89', booking: { customer: $('Processar Resposta da IA').item.json.customerName, service: $('Processar Resposta da IA').item.json.serviceType, dateTime: $('Processar Resposta da IA').item.json.interpretedDate, calendarEventId: $json.id } }) }}"
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.2,
"position": [
1340,
300
],
"id": "respond-success",
"name": "Responder - Agendamento Confirmado"
},
{
"parameters": {
"respondWith": "json",
"options": {
"responseCode": 500,
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
},
"responseBody": "={{ JSON.stringify({ success: false, message: 'N\u00e3o foi poss\u00edvel completar o agendamento. Por favor, tente novamente ou entre em contato via WhatsApp.', error: $json.message || 'Erro desconhecido' }) }}"
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.2,
"position": [
1340,
500
],
"id": "respond-error",
"name": "Responder - Erro"
},
{
"parameters": {
"respondWith": "json",
"options": {
"responseCode": 200,
"responseHeaders": {
"entries": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
},
"responseBody": "={{ JSON.stringify({ available: $json.available, message: $json.message, suggestedTimes: $json.suggestedTimes }) }}"
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.2,
"position": [
1340,
600
],
"id": "respond-availability",
"name": "Responder - Disponibilidade"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
680,
480
],
"id": "google-gemini-model",
"name": "Google Gemini Chat Model",
"credentials": {
"googleGeminiApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Webhook - Verificar Disponibilidade": {
"main": [
[
{
"node": "Extrair Dados de Disponibilidade",
"type": "main",
"index": 0
}
]
]
},
"Webhook - Agendar": {
"main": [
[
{
"node": "Extrair Dados do Booking",
"type": "main",
"index": 0
}
]
]
},
"Extrair Dados do Booking": {
"main": [
[
{
"node": "IA - Converter Texto para Data",
"type": "main",
"index": 0
}
]
]
},
"Extrair Dados de Disponibilidade": {
"main": [
[
{
"node": "IA - Verificar Disponibilidade",
"type": "main",
"index": 0
}
]
]
},
"IA - Converter Texto para Data": {
"main": [
[
{
"node": "Processar Resposta da IA",
"type": "main",
"index": 0
}
]
]
},
"IA - Verificar Disponibilidade": {
"main": [
[
{
"node": "Google Calendar - Listar Eventos",
"type": "main",
"index": 0
}
]
]
},
"Processar Resposta da IA": {
"main": [
[
{
"node": "Google Calendar - Criar Evento",
"type": "main",
"index": 0
}
]
]
},
"Google Calendar - Criar Evento": {
"main": [
[
{
"node": "Responder - Agendamento Confirmado",
"type": "main",
"index": 0
}
]
],
"error": [
[
{
"node": "Responder - Erro",
"type": "main",
"index": 0
}
]
]
},
"Google Calendar - Listar Eventos": {
"main": [
[
{
"node": "Processar Disponibilidade",
"type": "main",
"index": 0
}
]
]
},
"Processar Disponibilidade": {
"main": [
[
{
"node": "Responder - Disponibilidade",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "IA - Converter Texto para Data",
"type": "ai_languageModel",
"index": 0
},
{
"node": "IA - Verificar Disponibilidade",
"type": "ai_languageModel",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"meta": {
"templateCredsSetupCompleted": true
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleCalendarOAuth2ApigoogleGeminiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Instituto Ariana Borges - Agendamento. Uses informationExtractor, googleCalendar, lmChatGoogleGemini. Webhook trigger; 14 nodes.
Source: https://github.com/designrique/site-ariana-borges/blob/e15c4e4681a5431848df0da719c0d8aacd5fbd4d/guias/n8n-workflow-agendamento-atualizado.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Agendamento Instituto Ariana Borges - Corrigido. Uses lmChatGoogleGemini, googleCalendar, informationExtractor. Webhook trigger; 14 nodes.
Agendamento Instituto Ariana Borges - Corrigido. Uses lmChatGoogleGemini, googleCalendar, informationExtractor. Webhook trigger; 14 nodes.
Who is this for? Event organizers, conference planners, and marketing teams fighting registration drop-off who want 4-field forms with LinkedIn-level attendee intelligence. What problem is this workfl
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
This n8n template automates scraping content from Skool communities using the Olostep API. It collects structured data from Skool pages and stores it in a clean format, making it easy to analyze commu