This workflow follows the Googlegemini → HTTP Request 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": "Lumos",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "start",
"responseMode": "responseNode",
"options": {
"allowedOrigins": "*",
"binaryPropertyName": "audio"
}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
0,
0
],
"id": "ca0b83a3-67fc-468d-bb60-a054e36df584",
"name": "Webhook",
"onError": "continueErrorOutput"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"status\": \"error\", \"code\": \"INVALID_REQUEST\", \"message\": \"Requ\u00eate invalide ou fichier audio manquant\"}",
"options": {
"responseCode": 400
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
0,
240
],
"id": "a1b2c3d4-0001-0001-0001-000000000001",
"name": "Webhook Error"
},
{
"parameters": {
"method": "POST",
"url": "http://host.docker.internal:8000/transcribe",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"parameterType": "formBinaryData",
"name": "audio",
"inputDataFieldName": "audio"
}
]
},
"options": {
"timeout": 30000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
280,
0
],
"id": "88ae1dd3-9852-4bb6-811e-6c50ceb791fe",
"name": "Whisper Transcription",
"onError": "continueErrorOutput"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"status\": \"error\", \"code\": \"WHISPER_OFFLINE\", \"message\": \"Service de transcription indisponible. V\u00e9rifiez que le serveur Whisper tourne sur le port 8000.\"}",
"options": {
"responseCode": 503
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
280,
240
],
"id": "a1b2c3d4-0001-0001-0001-000000000002",
"name": "Whisper Error"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "models/gemini-2.5-flash",
"mode": "list",
"cachedResultName": "models/gemini-2.5-flash"
},
"messages": {
"values": [
{
"content": "=Tu es un assistant domotique. Analyse cette commande vocale et retourne UNIQUEMENT un tableau JSON valide, sans markdown, sans explication, sans backticks.\n\nPi\u00e8ces disponibles :\n- salon (pin 2)\n- cuisine (pin 6)\n- chambre_paul (pin 8)\n- chambre_principale (pin 13)\n\nActions possibles : on, off\n\nR\u00e8gle importante : retourne TOUJOURS un tableau, m\u00eame pour une seule action.\n\nExemples de r\u00e9ponses attendues :\n\"allume le salon\" -> [{\"room\": \"salon\", \"action\": \"on\", \"pin\": 2}]\n\"\u00e9teins tout\" -> [{\"room\": \"salon\", \"action\": \"off\", \"pin\": 2}, {\"room\": \"cuisine\", \"action\": \"off\", \"pin\": 6}, {\"room\": \"chambre_paul\", \"action\": \"off\", \"pin\": 8}, {\"room\": \"chambre_principale\", \"action\": \"off\", \"pin\": 13}]\n\"allume la chambre de paul et le salon\" -> [{\"room\": \"chambre_paul\", \"action\": \"on\", \"pin\": 8}, {\"room\": \"salon\", \"action\": \"on\", \"pin\": 2}]\n\nSi la commande est incompr\u00e9hensible ou ne concerne pas une lumi\u00e8re :\n[{\"error\": \"commande non reconnue\"}]\n\nCommande vocale : {{ $json.text }}"
}
]
},
"jsonOutput": true,
"builtInTools": {},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.googleGemini",
"typeVersion": 1.1,
"position": [
560,
0
],
"id": "d9e04136-7e58-4b8b-91f9-293d0abdeacc",
"name": "Gemini LLM",
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"status\": \"error\", \"code\": \"LLM_ERROR\", \"message\": \"Erreur lors de l'interpr\u00e9tation de la commande. V\u00e9rifiez votre cl\u00e9 API Gemini.\"}",
"options": {
"responseCode": 502
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
560,
240
],
"id": "a1b2c3d4-0001-0001-0001-000000000003",
"name": "Gemini Error"
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\n\n// Gemini jsonOutput:true retourne directement l'objet/tableau\n// mais on g\u00e8re aussi le cas string au cas o\u00f9\nlet parsed;\nif (typeof data === 'string') {\n const cleaned = data\n .replace(/```json/g, '')\n .replace(/```/g, '')\n .replace(/`/g, '')\n .trim();\n parsed = JSON.parse(cleaned);\n} else {\n parsed = data;\n}\n\n// Normaliser en tableau\nconst commands = Array.isArray(parsed) ? parsed : [parsed];\n\n// R\u00e9cup\u00e9rer la transcription depuis le node Whisper\nconst transcription = $('Whisper Transcription').first().json.text || '';\n\n// V\u00e9rifier si Gemini a retourn\u00e9 une erreur\nif (commands.length === 1 && commands[0].error) {\n return [{\n json: {\n status: 'error',\n code: 'COMMAND_NOT_RECOGNIZED',\n message: 'Commande non reconnue : ' + commands[0].error,\n transcription: transcription\n }\n }];\n}\n\n// Succ\u00e8s\nreturn [{\n json: {\n status: 'success',\n transcription: transcription,\n commands: commands\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
840,
0
],
"id": "3b6b0210-857a-4cf0-8cbe-f2176e8b38a9",
"name": "Parse Commands",
"onError": "continueErrorOutput"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"status\": \"error\", \"code\": \"PARSE_ERROR\", \"message\": \"Impossible de parser la r\u00e9ponse du LLM.\"}",
"options": {
"responseCode": 500
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
840,
240
],
"id": "a1b2c3d4-0001-0001-0001-000000000004",
"name": "Parse Error"
},
{
"parameters": {
"respondWith": "firstIncomingItem",
"options": {
"responseCode": 200
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1120,
0
],
"id": "a1b2c3d4-0001-0001-0001-000000000005",
"name": "Success Response"
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Whisper Transcription",
"type": "main",
"index": 0
}
],
[
{
"node": "Webhook Error",
"type": "main",
"index": 0
}
]
]
},
"Whisper Transcription": {
"main": [
[
{
"node": "Gemini LLM",
"type": "main",
"index": 0
}
],
[
{
"node": "Whisper Error",
"type": "main",
"index": 0
}
]
]
},
"Gemini LLM": {
"main": [
[
{
"node": "Parse Commands",
"type": "main",
"index": 0
}
],
[
{
"node": "Gemini Error",
"type": "main",
"index": 0
}
]
]
},
"Parse Commands": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
],
[
{
"node": "Parse Error",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"versionId": "68f73fc4-9760-4e7b-b5e6-07eff4c43d78",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "Fl2dg3L5i5Ip1YLZ",
"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.
googlePalmApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Lumos. Uses httpRequest, googleGemini. Webhook trigger; 9 nodes.
Source: https://github.com/Kimbohy/lumos/blob/88c1160f628c0a0d6bba4a7bb073a53ea12ffdb1/n8n/Lumos.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.
This workflow automates the process of generating stylized product photos for e-commerce by combining real product shots with creative templates. It enables the creation of a complete set of images fo
How it works Runs on schedule (Monday-Friday at 9 AM) to automate lead generation Searches for companies on Google Maps by location and category Extracts owner information from company websites and im
This workflow is designed for creators, designers, and automation builders who need to generate visually consistent images at scale. It is ideal for teams producing branded visuals, social media asset
This workflow creates high-quality, text-rich advertising banners from simple LINE messages.
This workflow turns complex data or topics sent via LINE into beautiful, easy-to-understand Infographics.