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 →
{
"id": "aicp-ask-webhook",
"name": "Ask a human (HTTP door for the runner)",
"settings": {
"errorWorkflow": "aicp-error-trigger"
},
"nodes": [
{
"id": "hook",
"name": "POST /ask-human",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
200,
240
],
"parameters": {
"httpMethod": "POST",
"path": "ask-human",
"responseMode": "onReceived",
"responseData": "accepted",
"options": {}
}
},
{
"id": "auth",
"name": "Authenticate + validate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
430,
240
],
"parameters": {
"jsCode": "// FAIL CLOSED on the token. This endpoint blocks an agent run and pages a human,\n// so an unauthenticated caller on the docker network could spend a person's\n// attention on demand.\nconst req = $input.first().json || {};\nconst headers = req.headers || {};\nconst want = $env.RUNNER_TOKEN || '';\nconst got = headers['x-runner-token'] || headers['X-Runner-Token'] || '';\n\nif (!want) {\n throw new Error('ask-webhook: RUNNER_TOKEN is not set in n8n \u2014 refusing to serve an unauthenticated question endpoint.');\n}\nif (!got || got !== want) {\n throw new Error('ask-webhook: bad or missing x-runner-token');\n}\n\nconst b = req.body || {};\nconst question = (b.question || '').trim();\nif (!question) {\n throw new Error('ask-webhook: `question` is required \u2014 refusing to ask a human nothing.');\n}\n\n// The ticket is a KEY, and keys from callers deserve an allowlist rather than a\n// sanitiser. UUID or nothing.\nconst ticket = String(b.ticket || '').trim();\nif (!/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(ticket)) {\n throw new Error('ask-webhook: `ticket` must be a UUID.');\n}\n\n// Bound the wait: the agent is blocked while this runs.\nconst MAX_MIN = 10;\nconst minutes = Math.min(Number(b.timeoutMinutes) > 0 ? Number(b.timeoutMinutes) : MAX_MIN, MAX_MIN);\n\nreturn [{ json: {\n ticket, question,\n context: b.context || '',\n source: b.source || 'agent',\n timeoutHours: minutes / 60,\n} }];"
}
},
{
"id": "slack",
"name": "Start Slack watch",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
660,
380
],
"onError": "continueRegularOutput",
"parameters": {
"url": "=http://lanes:8081/slack-ask",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "ticket",
"value": "={{ $json.ticket }}"
},
{
"name": "question",
"value": "={{ $json.question }}"
},
{
"name": "context",
"value": "={{ $json.context }}"
},
{
"name": "minutes",
"value": "={{ Math.round($json.timeoutHours * 60) }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-lanes-token",
"value": "={{ $env.LANES_TOKEN }}"
}
]
},
"options": {
"timeout": 20000
}
}
},
{
"id": "ask",
"name": "Ask the human",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
660,
240
],
"parameters": {
"source": "database",
"workflowId": {
"__rl": true,
"value": "aicp-ask-question",
"mode": "id"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {
"question": "={{ $json.question }}",
"context": "={{ $json.context }}",
"source": "={{ $json.source }}",
"timeoutHours": "={{ $json.timeoutHours }}"
},
"matchingColumns": [],
"schema": [
{
"id": "question",
"displayName": "question",
"type": "string",
"required": true,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true
},
{
"id": "context",
"displayName": "context",
"type": "string",
"required": true,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true
},
{
"id": "source",
"displayName": "source",
"type": "string",
"required": true,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true
},
{
"id": "timeoutHours",
"displayName": "timeoutHours",
"type": "number",
"required": true,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": true
},
"options": {
"waitForSubWorkflow": true
}
}
},
{
"id": "shape",
"name": "Shape answer",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
890,
240
],
"parameters": {
"jsCode": "// Shape the answer for delivery. `answered` stays separate from `answer` all the\n// way to the agent: \"nobody answered\" and \"answered with nothing\" are different\n// facts, and a caller that cannot tell them apart treats silence as data.\nconst a = $input.first().json || {};\nconst t = $('Authenticate + validate').first().json || {};\nreturn [{ json: {\n ticket: t.ticket,\n provider: 'claude',\n answered: a.answered === true,\n answer: a.answer || '',\n timedOut: a.timedOut === true,\n note: a.note || '',\n} }];"
}
},
{
"id": "deliver",
"name": "Deliver via router",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1120,
240
],
"parameters": {
"method": "POST",
"url": "http://router:8080/ask/answer",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-router-token",
"value": "={{ $env.ROUTER_TOKEN }}"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 30000
}
}
}
],
"connections": {
"POST /ask-human": {
"main": [
[
{
"node": "Authenticate + validate",
"type": "main",
"index": 0
}
]
]
},
"Authenticate + validate": {
"main": [
[
{
"node": "Start Slack watch",
"type": "main",
"index": 0
},
{
"node": "Ask the human",
"type": "main",
"index": 0
}
]
]
},
"Ask the human": {
"main": [
[
{
"node": "Shape answer",
"type": "main",
"index": 0
}
]
]
},
"Shape answer": {
"main": [
[
{
"node": "Deliver via router",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"description": "HTTP door for a runner to raise a question mid-run. Responds immediately, starts a Slack thread watch alongside the form, and hands the answer back through the router rather than straight to a runner."
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Ask a human (HTTP door for the runner). Uses httpRequest. Webhook trigger; 6 nodes.
Source: https://github.com/jgobuilds/ai-control-plane-public/blob/main/n8n-workflows/ask-webhook.workflow.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 n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c