AutomationFlowsGeneral › Rodopi Dent - Calendar Update Event

Rodopi Dent - Calendar Update Event

Rodopi Dent - Calendar Update Event. Uses googleCalendar. Webhook trigger; 5 nodes.

Webhook trigger★★★★☆ complexity5 nodesGoogle Calendar
General Trigger: Webhook Nodes: 5 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "name": "Rodopi Dent - Calendar Update Event",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "calendar-update",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "*"
        }
      },
      "id": "webhook",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse and validate input\nconst body = $input.first().json.body;\n\n// Required: event ID\nif (!body.eventId && !body.id && !body.googleEventId) {\n  throw new Error('ID \u043d\u0430 \u0441\u044a\u0431\u0438\u0442\u0438\u0435\u0442\u043e \u0435 \u0437\u0430\u0434\u044a\u043b\u0436\u0438\u0442\u0435\u043b\u043d\u043e');\n}\n\nconst eventId = body.eventId || body.id || body.googleEventId;\n\n// Map color names to Google Calendar colorId (1-11)\nconst colorMap = {\n  'green': '10', 'blue': '9', 'red': '11', 'yellow': '5',\n  'purple': '3', 'orange': '6', 'pink': '4', 'gray': '8'\n};\n\nconst statusLabels = { 'pending': '\u0427\u0430\u043a\u0430\u0449', 'confirmed': '\u041f\u043e\u0442\u0432\u044a\u0440\u0434\u0435\u043d', 'completed': '\u0417\u0430\u0432\u044a\u0440\u0448\u0435\u043d' };\nconst statusVal = body.status || 'confirmed';\nconst statusLabel = statusLabels[statusVal] || '\u041f\u043e\u0442\u0432\u044a\u0440\u0434\u0435\u043d';\n\n// Reminder flag (if provided)\nconst hasReminderField = (body.sendReminder !== undefined && body.sendReminder !== null);\nconst sendReminder = body.sendReminder === true || body.sendReminder === 'true';\n\n// Build update fields\nconst updateFields = {};\n\nif (body.date && body.startTime) {\n  const duration = body.duration || 30;\n  const startDate = new Date(`${body.date}T${body.startTime}:00`);\n  const endDate = new Date(startDate.getTime() + duration * 60 * 1000);\n  const endTime = body.endTime || `${String(endDate.getHours()).padStart(2, '0')}:${String(endDate.getMinutes()).padStart(2, '0')}`;\n  \n  updateFields.startDateTime = `${body.date}T${body.startTime}:00`;\n  updateFields.endDateTime = `${body.date}T${endTime}:00`;\n}\n\nif (body.patientName) {\n  let name = body.patientName.replace(/^\u23f3\\s*/, '');\n  updateFields.summary = statusVal === 'pending' ? '\u23f3 ' + name : name;\n}\n\n// Build description with status line AND reminder tag\nlet description;\nif (body.description) {\n  // Caller provided full description \u2014 clean known tags then re-append\n  description = body.description\n    .replace(/\ud83d\udccb\\s*\u0421\u0442\u0430\u0442\u0443\u0441:.*\\n?/gi, '')\n    .replace(/\ud83d\udd14\\s*\u041d\u0430\u043f\u043e\u043c\u043d\u044f\u043d\u0435:.*\\n?/gi, '')\n    .trim();\n  description += '\\n\ud83d\udccb \u0421\u0442\u0430\u0442\u0443\u0441: ' + statusLabel;\n  if (hasReminderField) {\n    description += '\\n\ud83d\udd14 \u041d\u0430\u043f\u043e\u043c\u043d\u044f\u043d\u0435: ' + (sendReminder ? '\u0414\u0410' : '\u041d\u0415');\n  }\n} else {\n  // Build description from individual fields\n  description = '';\n  if (body.patientPhone) description += `\ud83d\udcde \u0422\u0435\u043b: ${body.patientPhone}\\n`;\n  if (body.patientEmail) description += `\u2709\ufe0f \u0418\u043c\u0435\u0439\u043b: ${body.patientEmail}\\n`;\n  if (body.procedure) description += `\ud83e\uddb7 \u041f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430: ${body.procedure}\\n`;\n  description += `\ud83d\udccb \u0421\u0442\u0430\u0442\u0443\u0441: ${statusLabel}\\n`;\n  if (hasReminderField) {\n    description += `\ud83d\udd14 \u041d\u0430\u043f\u043e\u043c\u043d\u044f\u043d\u0435: ${sendReminder ? '\u0414\u0410' : '\u041d\u0415'}\\n`;\n  }\n  if (body.notes) description += `\ud83d\udcdd \u0411\u0435\u043b\u0435\u0436\u043a\u0438: ${body.notes}\\n`;\n}\nupdateFields.description = description.trim();\n\nconst statusColorDefaults = { 'pending': 'yellow', 'completed': 'gray', 'confirmed': 'green' };\nconst effectiveColor = body.colorId || statusColorDefaults[statusVal] || 'green';\nupdateFields.colorId = colorMap[effectiveColor] || colorMap['green'];\n\nreturn [{\n  json: {\n    eventId: eventId,\n    updateFields: updateFields,\n    status: statusVal,\n    sendReminder: sendReminder,\n    originalData: body\n  }\n}];"
      },
      "id": "prepare",
      "name": "Prepare Update Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "rodopi.dent@gmail.com"
        },
        "eventId": "={{ $json.eventId }}",
        "updateFields": {
          "start": "={{ $json.updateFields.startDateTime }}",
          "end": "={{ $json.updateFields.endDateTime }}",
          "summary": "={{ $json.updateFields.summary }}",
          "description": "={{ $json.updateFields.description }}",
          "colorId": "={{ $json.updateFields.colorId || '' }}"
        }
      },
      "id": "update-event",
      "name": "Update Calendar Event",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1.2,
      "position": [
        440,
        0
      ],
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Get updated event details\nconst event = $input.first().json;\nconst prevData = $('Prepare Update Data').first().json;\n\n// Helper function to convert to Europe/Sofia timezone\nfunction toSofiaTime(isoString) {\n  if (!isoString) return null;\n  const d = new Date(isoString);\n  const options = {\n    timeZone: 'Europe/Sofia',\n    year: 'numeric',\n    month: '2-digit',\n    day: '2-digit',\n    hour: '2-digit',\n    minute: '2-digit',\n    hour12: false\n  };\n  const formatter = new Intl.DateTimeFormat('en-CA', options);\n  const parts = formatter.formatToParts(d);\n  const getPart = (type) => parts.find(p => p.type === type)?.value || '00';\n  return {\n    date: `${getPart('year')}-${getPart('month')}-${getPart('day')}`,\n    time: `${getPart('hour')}:${getPart('minute')}`\n  };\n}\n\n// Parse times - CONVERT TO SOFIA TIMEZONE\nconst startDateTime = event.start?.dateTime || event.start?.date;\nconst endDateTime = event.end?.dateTime || event.end?.date;\nlet startDate, startTime, endTime;\n\nif (startDateTime) {\n  const startSofia = toSofiaTime(startDateTime);\n  const endSofia = toSofiaTime(endDateTime);\n  startDate = startSofia.date;\n  startTime = startSofia.time;\n  endTime = endSofia.time;\n}\n\nreturn [{\n  json: {\n    id: event.id,\n    googleEventId: event.id,\n    title: event.summary,\n    date: startDate,\n    startTime: startTime,\n    endTime: endTime,\n    description: event.description,\n    status: prevData.status || 'confirmed',\n    htmlLink: event.htmlLink,\n    updated: event.updated\n  }\n}];"
      },
      "id": "format-response",
      "name": "Format Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, message: '\u0421\u044a\u0431\u0438\u0442\u0438\u0435\u0442\u043e \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e', event: $json }) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      },
      "id": "respond-success",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        0
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Prepare Update Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Update Data": {
      "main": [
        [
          {
            "node": "Update Calendar Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Calendar Event": {
      "main": [
        [
          {
            "node": "Format Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Response": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "Rodopi Dent"
    }
  ]
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Rodopi Dent - Calendar Update Event. Uses googleCalendar. Webhook trigger; 5 nodes.

Source: https://github.com/Georgi-Piskov/RODOPI-DENT/blob/9eac796b44530dafd0ccdec3388d9825197936ea/n8n-workflows/13-calendar-update.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

github code Try yourself

Google Calendar
General

Connect a Vapi AI voice agent to Google Calendar to capture contact details and auto-book appointments. The agent asks for name, address, service type, and a preferred time. The workflow checks availa

Google Calendar
General

n8nCal · /events. Uses googleCalendar. Webhook trigger; 9 nodes.

Google Calendar
General

Need help? Want access to this workflow + many more paid workflows + live Q&A sessions with a top verified n8n creator?

Google Calendar
General

A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without

Crypto, Data Table, Execute Workflow Trigger