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": "Multi-Agent Android Pipeline",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "android-task",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-trigger",
"name": "Webhook Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"functionCode": "// Parse and validate incoming task request\nconst body = $input.item.json.body || $input.item.json;\n\nif (!body.task) {\n throw new Error('Missing required field: task');\n}\n\nreturn {\n json: {\n task: body.task,\n deviceId: body.device_id || null,\n priority: body.priority || 'normal',\n timeout: body.timeout || 120,\n callbackUrl: body.callback_url || null\n }\n};"
},
"id": "parse-request",
"name": "Parse Request",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
450,
300
]
},
{
"parameters": {
"method": "GET",
"url": "http://localhost:8080/api/v1/devices",
"options": {}
},
"id": "list-devices",
"name": "List Devices",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
650,
300
]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.count }}",
"operation": "larger",
"value2": 0
}
]
}
},
"id": "check-devices",
"name": "Devices Available?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
850,
300
]
},
{
"parameters": {
"functionCode": "// Select best device for task\nconst devices = $input.item.json.devices || [];\nconst requestedDevice = $('Parse Request').item.json.deviceId;\n\nlet selectedDevice = null;\n\n// If specific device requested\nif (requestedDevice) {\n selectedDevice = devices.find(d => d.id === requestedDevice);\n}\n\n// Otherwise, select first connected device\nif (!selectedDevice) {\n selectedDevice = devices.find(d => d.status === 'connected');\n}\n\nif (!selectedDevice) {\n throw new Error('No suitable device available');\n}\n\nreturn {\n json: {\n device: selectedDevice,\n task: $('Parse Request').item.json.task,\n timeout: $('Parse Request').item.json.timeout\n }\n};"
},
"id": "select-device",
"name": "Select Device",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
1050,
200
]
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:8080/api/v1/tasks",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"description\": \"{{ $json.task }}\",\n \"device_id\": \"{{ $json.device.id }}\",\n \"timeout\": {{ $json.timeout }},\n \"webhook_url\": \"{{ $('Parse Request').item.json.callbackUrl }}\"\n}",
"options": {
"timeout": 30000
}
},
"id": "create-task",
"name": "Create Task",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1250,
200
]
},
{
"parameters": {
"loopCount": 30,
"options": {
"reset": false
}
},
"id": "poll-loop",
"name": "Poll Loop",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
1450,
200
]
},
{
"parameters": {
"amount": 2,
"unit": "seconds"
},
"id": "poll-wait",
"name": "Poll Wait",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1650,
200
]
},
{
"parameters": {
"method": "GET",
"url": "=http://localhost:8080/api/v1/tasks/{{ $('Create Task').item.json.task.id }}",
"options": {}
},
"id": "get-status",
"name": "Get Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
1850,
200
]
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.task.status }}",
"operation": "contains",
"value2": "completed"
}
]
}
},
"id": "is-complete",
"name": "Is Complete?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
2050,
200
]
},
{
"parameters": {
"functionCode": "// Format successful response\nconst task = $input.item.json.task;\nconst result = task.result || {};\n\nreturn {\n json: {\n status: 'success',\n taskId: task.id,\n deviceId: task.device_id,\n description: task.description,\n executionTime: result.duration_seconds,\n steps: result.steps || [],\n screenshot: result.final_screenshot,\n timestamp: new Date().toISOString()\n }\n};"
},
"id": "format-success",
"name": "Format Success",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
2250,
100
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"id": "respond-success",
"name": "Respond Success",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
2450,
100
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={\n \"status\": \"error\",\n \"message\": \"No devices available\"\n}",
"options": {
"responseCode": 503
}
},
"id": "no-devices-response",
"name": "No Devices Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
1050,
400
]
}
],
"connections": {
"Webhook Trigger": {
"main": [
[
{
"node": "Parse Request",
"type": "main",
"index": 0
}
]
]
},
"Parse Request": {
"main": [
[
{
"node": "List Devices",
"type": "main",
"index": 0
}
]
]
},
"List Devices": {
"main": [
[
{
"node": "Devices Available?",
"type": "main",
"index": 0
}
]
]
},
"Devices Available?": {
"main": [
[
{
"node": "Select Device",
"type": "main",
"index": 0
}
],
[
{
"node": "No Devices Response",
"type": "main",
"index": 0
}
]
]
},
"Select Device": {
"main": [
[
{
"node": "Create Task",
"type": "main",
"index": 0
}
]
]
},
"Create Task": {
"main": [
[
{
"node": "Poll Loop",
"type": "main",
"index": 0
}
]
]
},
"Poll Loop": {
"main": [
[
{
"node": "Poll Wait",
"type": "main",
"index": 0
}
]
]
},
"Poll Wait": {
"main": [
[
{
"node": "Get Status",
"type": "main",
"index": 0
}
]
]
},
"Get Status": {
"main": [
[
{
"node": "Is Complete?",
"type": "main",
"index": 0
}
]
]
},
"Is Complete?": {
"main": [
[
{
"node": "Format Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Poll Loop",
"type": "main",
"index": 0
}
]
]
},
"Format Success": {
"main": [
[
{
"node": "Respond Success",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
"android",
"automation",
"gelab",
"multi-agent"
],
"triggerCount": 0,
"updatedAt": "2025-01-01T00:00:00.000Z",
"versionId": "1"
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Multi-Agent Android Pipeline. Uses httpRequest. Webhook trigger; 13 nodes.
Source: https://github.com/thamizhelango/droid-flow/blob/e9dafcd70af69e1a7d8868816a26a93470965bfa/n8n-workflows/multi-agent-pipeline.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.