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": "delete_song_webhook",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "100e58e9-a889-436a-8ad4-c8c361580a31",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
0
],
"id": "f64f1d41-003f-499b-9b9e-c471bf8a7a37",
"name": "Webhook - Delete Song"
},
{
"parameters": {
"jsCode": "const item = $input.first().json;\n\nconst rawQuery =\n typeof item.body?.query === \"string\" ? item.body.query :\n typeof item.query === \"string\" ? item.query :\n \"\";\n\nconst explicitSong =\n typeof item.body?.song === \"string\" ? item.body.song :\n typeof item.song === \"string\" ? item.song :\n \"\";\n\nconst explicitArtist =\n typeof item.body?.artist === \"string\" ? item.body.artist :\n typeof item.artist === \"string\" ? item.artist :\n \"\";\n\nlet song = explicitSong.trim();\nlet artist = explicitArtist.trim();\n\nif ((!song || !artist) && rawQuery) {\n const cleaned = rawQuery\n .replace(/^\\s*(delete|remove|slet)\\s+/i, \"\")\n .trim();\n\n const byMatch = cleaned.match(/^(.*?)\\s+by\\s+(.*?)$/i);\n\n if (byMatch) {\n song = song || byMatch[1].trim();\n artist = artist || byMatch[2].trim();\n }\n}\n\nreturn [\n {\n json: {\n rawQuery: rawQuery.trim(),\n song,\n artist,\n readyToDelete: Boolean(song && artist)\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
208,
0
],
"id": "276cd217-ccc0-4fb8-85ea-dd1e901f048c",
"name": "Prepare Delete Query"
},
{
"parameters": {
"jsCode": "const item = $input.first().json;\n\nconst song = item.song || \"\";\nconst artist = item.artist || \"\";\nconst rawQuery = item.rawQuery || \"\";\nconst readyToDelete = Boolean(item.readyToDelete);\n\nconst rawBody = JSON.stringify({\n statement: `\n MATCH (s:Song)-[:BY_ARTIST]->(a:Artist)\n WHERE toLower(a.name) = toLower($artist)\n AND toLower(s.title) CONTAINS toLower($song)\n WITH collect(s) AS songs,\n collect(s.title) AS songTitles,\n collect(a) AS artistNodes,\n collect(a.name) AS artistNames\n WITH songs, songTitles, artistNodes, artistNames, size(songs) AS matchCount\n WITH\n CASE WHEN matchCount = 1 THEN songs[0] ELSE null END AS songNode,\n CASE WHEN matchCount = 1 THEN songTitles[0] ELSE '' END AS deletedSong,\n CASE WHEN matchCount = 1 THEN artistNodes[0] ELSE null END AS artistNode,\n CASE WHEN matchCount = 1 THEN artistNames[0] ELSE '' END AS deletedArtist,\n matchCount\n FOREACH (node IN CASE WHEN songNode IS NOT NULL THEN [songNode] ELSE [] END | DETACH DELETE node)\n WITH artistNode, deletedSong, deletedArtist, matchCount\n OPTIONAL MATCH (remaining:Song)-[:BY_ARTIST]->(artistNode)\n WITH artistNode, deletedSong, deletedArtist, matchCount, count(remaining) AS remainingSongs\n FOREACH (node IN CASE WHEN artistNode IS NOT NULL AND remainingSongs = 0 THEN [artistNode] ELSE [] END | DELETE node)\n RETURN deletedSong, deletedArtist, matchCount\n `.replace(/\\s+/g, \" \").trim(),\n parameters: {\n song,\n artist\n }\n});\n\nreturn [\n {\n json: {\n rawQuery,\n song,\n artist,\n readyToDelete,\n rawBody\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
432,
0
],
"id": "e86716d8-59a4-4cf1-98c2-dc3eb1fce169",
"name": "Build Neo4j Delete Request Body"
},
{
"parameters": {
"method": "POST",
"url": "http://host.docker.internal:7474/db/neo4j/query/v2",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Accept",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "=application/json",
"body": "={{$json.rawBody}}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
640,
0
],
"id": "149e9066-c10b-4778-b9ae-684bdf49039b",
"name": "Query Neo4j Delete",
"credentials": {
"httpBasicAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const item = $input.first().json;\nconst fields = item.data?.fields || [];\nconst values = item.data?.values || [];\nconst rawQuery = $('Prepare Delete Query').first().json.rawQuery || \"\";\nconst requestedSong = $('Prepare Delete Query').first().json.song || \"\";\nconst requestedArtist = $('Prepare Delete Query').first().json.artist || \"\";\nconst readyToDelete = $('Prepare Delete Query').first().json.readyToDelete || false;\n\nfunction mapRow(row) {\n const mapped = {};\n fields.forEach((field, index) => {\n mapped[field] = row[index];\n });\n return mapped;\n}\n\nif (!readyToDelete) {\n return [\n {\n json: {\n found: false,\n deleted: false,\n response: \"I need both a song title and an artist to delete a stored song safely.\"\n }\n }\n ];\n}\n\nif (!values.length) {\n return [\n {\n json: {\n found: false,\n deleted: false,\n response: `I couldn't find a stored song matching \"${rawQuery || `${requestedSong} by ${requestedArtist}`}\".`\n }\n }\n ];\n}\n\nconst result = mapRow(values[0]);\nconst matchCount = Number(result.matchCount || 0);\n\nif (matchCount === 1) {\n return [\n {\n json: {\n found: true,\n deleted: true,\n deletedSong: result.deletedSong,\n deletedArtist: result.deletedArtist,\n response: `Deleted \"${result.deletedSong}\" by ${result.deletedArtist} from storage.`\n }\n }\n ];\n}\n\nif (matchCount > 1) {\n return [\n {\n json: {\n found: true,\n deleted: false,\n response: `I found multiple matches for \"${requestedSong}\" by ${requestedArtist}, so I did not delete anything. Please be more specific.`\n }\n }\n ];\n}\n\nreturn [\n {\n json: {\n found: false,\n deleted: false,\n response: `I couldn't find a stored song matching \"${rawQuery || `${requestedSong} by ${requestedArtist}`}\".`\n }\n }\n];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
848,
0
],
"id": "42427dbb-47f1-46b1-b4b3-e1424540a977",
"name": "Format Delete Result"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{$json}}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1056,
0
],
"id": "9f01ef2a-c3a5-4ed1-8bbc-d5add740f863",
"name": "Respond - Delete Result"
}
],
"connections": {
"Webhook - Delete Song": {
"main": [
[
{
"node": "Prepare Delete Query",
"type": "main",
"index": 0
}
]
]
},
"Prepare Delete Query": {
"main": [
[
{
"node": "Build Neo4j Delete Request Body",
"type": "main",
"index": 0
}
]
]
},
"Build Neo4j Delete Request Body": {
"main": [
[
{
"node": "Query Neo4j Delete",
"type": "main",
"index": 0
}
]
]
},
"Query Neo4j Delete": {
"main": [
[
{
"node": "Format Delete Result",
"type": "main",
"index": 0
}
]
]
},
"Format Delete Result": {
"main": [
[
{
"node": "Respond - Delete Result",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "4e089bb1-dcd0-4410-be9a-23db9d1591ff",
"id": "rWJOlHmCwA4m0d1Z",
"tags": []
}
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.
httpBasicAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
delete_song_webhook. Uses httpRequest. Webhook trigger; 6 nodes.
Source: https://github.com/AndLOLGG/ai-chord-neo4j-agent/blob/92988c1277a189f2339216ab9b4eb9fc9d90ad74/n8n/delete_song_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.
Jigsaw API key for image processing, I use this as a gatekeeper/second pair of eyes. LINK to their website https://jigsawstack.com/ SECOND A postgress DATABASE (I use Supabase) LlamaCloud for the pars
Onsite Photos to Jobs (SMS Agent). Uses dataTable, twilio, httpRequest, airtable. Webhook trigger; 62 nodes.
W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 50 nodes.
W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 48 nodes.