AutomationFlowsData & Sheets › Rodopi Dent - Confirm/update Appointment

Rodopi Dent - Confirm/update Appointment

Rodopi Dent - Confirm/Update Appointment. Uses googleSheets. Webhook trigger; 8 nodes.

Webhook trigger★★★★☆ complexity8 nodesGoogle Sheets
Data & Sheets Trigger: Webhook Nodes: 8 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 - Confirm/Update Appointment",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "confirm-webhook",
        "responseMode": "responseNode",
        "options": {
          "allowedOrigins": "*"
        }
      },
      "id": "webhook-confirm",
      "name": "Webhook - Confirm",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1hv4XAfHhScA40Bm1kQ3I-Ih4SJuCBpOJxTOYDNb167g"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Appointments"
        },
        "options": {}
      },
      "id": "sheets-read",
      "name": "Read Appointments",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        220,
        0
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const body = $('Webhook - Confirm').first().json.body;\nconst appointmentId = body.appointmentId;\nconst newStatus = body.status;\nconst newDuration = body.duration;\n\nif (!appointmentId || !newStatus) {\n  return [{ json: { success: false, error: 'appointmentId and status are required', found: false } }];\n}\n\nconst validStatuses = ['pending', 'confirmed', 'completed', 'cancelled', 'expired'];\nif (!validStatuses.includes(newStatus)) {\n  return [{ json: { success: false, error: `Invalid status. Must be one of: ${validStatuses.join(', ')}`, found: false } }];\n}\n\nconst appointments = $('Read Appointments').all();\nlet appointment = null;\n\nfor (const apt of appointments) {\n  if (apt.json.id === appointmentId) {\n    appointment = apt.json;\n    break;\n  }\n}\n\nif (!appointment) {\n  return [{ json: { success: false, error: 'Appointment not found', found: false } }];\n}\n\nlet hasConflict = false;\nlet conflictWith = null;\n\nif (newStatus === 'confirmed' && newDuration) {\n  function timeToMinutes(time) {\n    const [hours, minutes] = time.split(':').map(Number);\n    return hours * 60 + minutes;\n  }\n  \n  const aptStart = timeToMinutes(appointment.startTime);\n  const aptEnd = aptStart + parseInt(newDuration);\n  \n  for (const other of appointments) {\n    if (other.json.id === appointment.id) continue;\n    if (other.json.date !== appointment.date) continue;\n    if (other.json.status === 'cancelled' || other.json.status === 'expired') continue;\n    \n    const otherStart = timeToMinutes(other.json.startTime);\n    const otherDuration = other.json.status === 'pending' ? 30 : (parseInt(other.json.duration) || 30);\n    const otherEnd = otherStart + otherDuration;\n    \n    if (aptStart < otherEnd && aptEnd > otherStart) {\n      hasConflict = true;\n      conflictWith = { time: other.json.startTime, name: other.json.patientName };\n      break;\n    }\n  }\n}\n\nif (hasConflict) {\n  return [{ json: { success: false, error: `\u041a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0441 ${conflictWith.name} \u0432 ${conflictWith.time}`, found: true, conflict: true } }];\n}\n\nreturn [{\n  json: {\n    id: appointmentId,\n    status: newStatus,\n    duration: newDuration ? String(newDuration) : appointment.duration,\n    updatedAt: new Date().toISOString(),\n    patientName: appointment.patientName,\n    patientPhone: appointment.patientPhone,\n    date: appointment.date,\n    startTime: appointment.startTime,\n    reason: appointment.reason || '',\n    createdAt: appointment.createdAt,\n    oldStatus: appointment.status,\n    found: true,\n    success: true\n  }\n}];"
      },
      "id": "code-find",
      "name": "Find and Validate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "success-check",
              "leftValue": "={{ $json.success }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-found",
      "name": "Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Prepare clean data for Google Sheets update\nconst data = $input.first().json;\nreturn [{\n  json: {\n    id: data.id,\n    patientName: data.patientName,\n    patientPhone: data.patientPhone,\n    date: data.date,\n    startTime: data.startTime,\n    duration: data.duration,\n    reason: data.reason,\n    status: data.status,\n    createdAt: data.createdAt,\n    updatedAt: data.updatedAt\n  }\n}];"
      },
      "id": "prepare-update",
      "name": "Prepare Update",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        -100
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1hv4XAfHhScA40Bm1kQ3I-Ih4SJuCBpOJxTOYDNb167g"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Appointments"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "id"
          ]
        },
        "options": {}
      },
      "id": "sheets-update",
      "name": "Update Appointment",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1100,
        -100
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: true, message: '\u0421\u0442\u0430\u0442\u0443\u0441\u044a\u0442 \u0435 \u043e\u0431\u043d\u043e\u0432\u0435\u043d', appointmentId: $('Find and Validate').item.json.id, newStatus: $('Find and Validate').item.json.status }) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      },
      "id": "response-success",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1320,
        -100
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ success: false, error: $json.error }) }}",
        "options": {
          "responseCode": 400,
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      },
      "id": "response-error",
      "name": "Respond Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        100
      ]
    }
  ],
  "connections": {
    "Webhook - Confirm": {
      "main": [
        [
          {
            "node": "Read Appointments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Appointments": {
      "main": [
        [
          {
            "node": "Find and Validate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find and Validate": {
      "main": [
        [
          {
            "node": "Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid?": {
      "main": [
        [
          {
            "node": "Prepare Update",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Update": {
      "main": [
        [
          {
            "node": "Update Appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Appointment": {
      "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 - Confirm/Update Appointment. Uses googleSheets. Webhook trigger; 8 nodes.

Source: https://github.com/Georgi-Piskov/RODOPI-DENT/blob/ad5b6829b5d1009f64d2cce3db8393d3c11947c2/n8n-workflows/04-confirm-webhook.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

Resume Screening & Behavioral Interviews with Gemini, Elevenlabs, & Notion ATS copy. Uses googleDrive, httpRequest, notion, formTrigger. Webhook trigger; 67 nodes.

Google Drive, HTTP Request, Notion +3
Data & Sheets

[SANTOBET] FLUXO TODO - BACKUP. Uses googleSheets, httpRequest, googleSheetsTrigger. Webhook trigger; 57 nodes.

Google Sheets, HTTP Request, Google Sheets Trigger
Data & Sheets

This workflow sends post-purchase review request emails for WooCommerce orders, stores expiring form links in Google Sheets, optionally shortens links with Dub.co and delays delivery, serves a hosted

HTTP Request, Google Sheets, Email Send
Data & Sheets

FLUXO DISPARO DATA E HORA. Uses itemLists, googleSheets, httpRequest. Webhook trigger; 48 nodes.

Item Lists, Google Sheets, HTTP Request
Data & Sheets

This workflow allows you to accept online payments via YooKassa and log both orders and transactions in Google Sheets — all without writing a single line of code. It supports full payment flow: produc

Google Sheets, HTTP Request