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": "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.
googleSheetsOAuth2Api
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 →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Resume Screening & Behavioral Interviews with Gemini, Elevenlabs, & Notion ATS copy. Uses googleDrive, httpRequest, notion, formTrigger. Webhook trigger; 67 nodes.
[SANTOBET] FLUXO TODO - BACKUP. Uses googleSheets, httpRequest, googleSheetsTrigger. Webhook trigger; 57 nodes.
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
FLUXO DISPARO DATA E HORA. Uses itemLists, googleSheets, httpRequest. Webhook trigger; 48 nodes.
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