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": "phase5ProposeTaskStatus",
"name": "31 - TOOL - Propose update_task_status",
"nodes": [
{
"parameters": {
"content": "## Write-risk proposal only\nThis workflow validates the requested status and confirms that the task exists. It stores a five-minute proposal but **does not update `tasks`**.\n\nThe confirmation workflow later consumes the exact stored task ID and status.",
"height": 280,
"width": 640,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-820,
-320
],
"id": "53000000-0000-4000-8000-000000000001",
"name": "Update proposal explanation"
},
{
"parameters": {
"inputSource": "workflowInputs",
"workflowInputs": {
"values": [
{
"name": "sessionId",
"type": "string"
},
{
"name": "taskId",
"type": "number"
},
{
"name": "status",
"type": "string"
}
]
}
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.2,
"position": [
-720,
80
],
"id": "53000000-0000-4000-8000-000000000002",
"name": "Tool Input"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "javaScript",
"jsCode": "const sessionId = typeof $json.sessionId === 'string' ? $json.sessionId.trim() : '';\nconst taskId = typeof $json.taskId === 'number' ? $json.taskId : Number($json.taskId);\nconst status = typeof $json.status === 'string' ? $json.status.trim().toLowerCase() : '';\nconst uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst statuses = ['backlog', 'todo', 'in_progress', 'blocked', 'done'];\nconst proposedInput = { taskId, status };\nlet error = null;\n\nif (!uuidPattern.test(sessionId)) {\n error = { code: 'INVALID_SESSION', message: 'The status proposal needs a valid conversation session.' };\n} else if (!Number.isInteger(taskId) || taskId <= 0) {\n error = { code: 'INVALID_TASK_ID', message: 'Task ID must be a positive whole number.' };\n} else if (!statuses.includes(status)) {\n error = { code: 'INVALID_STATUS', message: `Status must be one of: ${statuses.join(', ')}.` };\n}\n\nreturn {\n json: {\n valid: error === null,\n sessionId,\n taskId,\n status,\n proposedInput,\n ...(error ? { response: { ok: false, error } } : {})\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-460,
80
],
"id": "53000000-0000-4000-8000-000000000003",
"name": "Validate Status Proposal"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "53000000-0000-4000-8000-00000000000f",
"leftValue": "={{ $json.valid }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-200,
80
],
"id": "53000000-0000-4000-8000-000000000004",
"name": "Proposal Is Valid?"
},
{
"parameters": {
"resource": "row",
"operation": "get",
"dataTableId": {
"__rl": true,
"value": "tasks",
"mode": "name"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyName": "id",
"condition": "eq",
"keyValue": "={{ $('Validate Status Proposal').item.json.taskId }}"
}
]
},
"returnAll": false,
"limit": 1,
"orderBy": false
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
60,
0
],
"id": "53000000-0000-4000-8000-000000000005",
"name": "Find Proposed Task",
"alwaysOutputData": true,
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "javaScript",
"jsCode": "const input = $('Validate Status Proposal').first().json;\nif (typeof $json.error === 'string' && $json.error !== '') {\n return {\n json: {\n ...input,\n ready: false,\n response: {\n ok: false,\n error: {\n code: 'TASK_DATA_UNAVAILABLE',\n message: 'Local task data is not ready. Rerun local setup, then try again.'\n }\n }\n }\n };\n}\nif (!Number.isInteger($json.id)) {\n return {\n json: {\n ...input,\n ready: false,\n response: {\n ok: false,\n error: {\n code: 'TASK_NOT_FOUND',\n message: `No local task exists with ID ${input.taskId}.`\n }\n }\n }\n };\n}\n\nconst actionId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (character) => { const random = Math.floor(Math.random() * 16); const value = character === 'x' ? random : (random & 0x3) | 0x8; return value.toString(16); });\nreturn {\n json: {\n ...input,\n ready: true,\n actionId,\n actionType: 'update_task_status',\n confirmationText: `CONFIRM ${actionId.replaceAll('-', '').slice(-8).toUpperCase()}`,\n expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString(),\n task: {\n id: $json.id,\n title: String($json.title ?? ''),\n currentStatus: String($json.status ?? '')\n }\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
320,
0
],
"id": "53000000-0000-4000-8000-000000000006",
"name": "Prepare Status Proposal"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "53000000-0000-4000-8000-00000000000f",
"leftValue": "={{ $json.ready }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
580,
0
],
"id": "53000000-0000-4000-8000-000000000007",
"name": "Task Is Ready?"
},
{
"parameters": {
"resource": "row",
"operation": "update",
"dataTableId": {
"__rl": true,
"value": "pending_actions",
"mode": "name"
},
"matchType": "allConditions",
"filters": {
"conditions": [
{
"keyName": "sessionId",
"condition": "eq",
"keyValue": "={{ $('Prepare Status Proposal').item.json.sessionId }}"
},
{
"keyName": "status",
"condition": "eq",
"keyValue": "pending"
}
]
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"status": "superseded",
"consumedAt": "={{ $now.toISO() }}"
},
"matchingColumns": [],
"schema": [
{
"id": "actionId",
"displayName": "actionId",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
},
{
"id": "sessionId",
"displayName": "sessionId",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
},
{
"id": "actionType",
"displayName": "actionType",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
},
{
"id": "proposedInput",
"displayName": "proposedInput",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
},
{
"id": "confirmationText",
"displayName": "confirmationText",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "string",
"removed": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "expiresAt",
"displayName": "expiresAt",
"required": false,
"defaultMatch": false,
"display": false,
"canBeUsedToMatch": true,
"type": "date",
"removed": true
},
{
"id": "consumedAt",
"displayName": "consumedAt",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "date"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"dryRun": false
}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
840,
-80
],
"id": "53000000-0000-4000-8000-000000000008",
"name": "Supersede Older Proposals",
"alwaysOutputData": true,
"onError": "continueRegularOutput"
},
{
"parameters": {
"resource": "row",
"operation": "insert",
"dataTableId": {
"__rl": true,
"value": "pending_actions",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"actionId": "={{ $('Prepare Status Proposal').item.json.actionId }}",
"sessionId": "={{ $('Prepare Status Proposal').item.json.sessionId }}",
"actionType": "update_task_status",
"proposedInput": "={{ JSON.stringify($('Prepare Status Proposal').item.json.proposedInput) }}",
"confirmationText": "={{ $('Prepare Status Proposal').item.json.confirmationText }}",
"status": "pending",
"expiresAt": "={{ $('Prepare Status Proposal').item.json.expiresAt }}"
},
"matchingColumns": [],
"schema": [
{
"id": "actionId",
"displayName": "actionId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "sessionId",
"displayName": "sessionId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "actionType",
"displayName": "actionType",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "proposedInput",
"displayName": "proposedInput",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "confirmationText",
"displayName": "confirmationText",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "expiresAt",
"displayName": "expiresAt",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "date"
},
{
"id": "consumedAt",
"displayName": "consumedAt",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "date"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
1100,
-80
],
"id": "53000000-0000-4000-8000-000000000009",
"name": "Insert Pending Action",
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "javaScript",
"jsCode": "const proposal = $('Prepare Status Proposal').first().json;\nif (!Number.isInteger($json.id)) {\n return {\n json: {\n ...proposal,\n response: {\n ok: false,\n error: {\n code: 'CONFIRMATION_DATA_UNAVAILABLE',\n message: 'The status proposal could not be saved. Rerun local setup, then try again.'\n }\n }\n }\n };\n}\nreturn {\n json: {\n ...proposal,\n response: {\n ok: true,\n confirmationRequired: true,\n action: {\n type: 'update_task_status',\n arguments: proposal.proposedInput,\n taskTitle: proposal.task.title,\n currentStatus: proposal.task.currentStatus\n },\n confirmation: {\n phrase: proposal.confirmationText,\n expiresAt: proposal.expiresAt\n },\n message: `No task changed. Ask the user to reply with exactly \"${proposal.confirmationText}\" within five minutes.`\n }\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1360,
-80
],
"id": "53000000-0000-4000-8000-00000000000a",
"name": "Shape Status Proposal"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "javaScript",
"jsCode": "const response = $json.response;\nconst proposedInput = $json.proposedInput ?? {};\nreturn {\n json: {\n occurredAt: new Date().toISOString(),\n sessionId: String($json.sessionId ?? ''),\n requestId: String($json.actionId ?? ''),\n toolName: 'update_task_status_proposal',\n proposedInput: JSON.stringify(proposedInput),\n result: JSON.stringify(response),\n error: response?.ok === false ? String(response.error?.message ?? 'Proposal failed') : '',\n response\n }\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1620,
80
],
"id": "53000000-0000-4000-8000-00000000000b",
"name": "Prepare Audit"
},
{
"parameters": {
"resource": "row",
"operation": "insert",
"dataTableId": {
"__rl": true,
"value": "tool_audit",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"occurredAt": "={{ $json.occurredAt }}",
"sessionId": "={{ $json.sessionId }}",
"requestId": "={{ $json.requestId }}",
"toolName": "={{ $json.toolName }}",
"proposedInput": "={{ $json.proposedInput }}",
"result": "={{ $json.result }}",
"error": "={{ $json.error }}"
},
"matchingColumns": [],
"schema": [
{
"id": "occurredAt",
"displayName": "occurredAt",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "date"
},
{
"id": "sessionId",
"displayName": "sessionId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "requestId",
"displayName": "requestId",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "toolName",
"displayName": "toolName",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "proposedInput",
"displayName": "proposedInput",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "result",
"displayName": "result",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
},
{
"id": "error",
"displayName": "error",
"required": false,
"defaultMatch": false,
"display": true,
"canBeUsedToMatch": true,
"type": "string"
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.dataTable",
"typeVersion": 1.1,
"position": [
1880,
80
],
"id": "53000000-0000-4000-8000-00000000000c",
"name": "Write Tool Audit",
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"language": "javaScript",
"jsCode": "const prepared = $('Prepare Audit').item.json;\nconst response = { ...prepared.response };\nif (!Number.isInteger($json.id)) {\n response.auditWarning = 'The proposal result could not be written to the local audit table.';\n}\nreturn { json: response };"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2140,
80
],
"id": "53000000-0000-4000-8000-00000000000d",
"name": "Return Tool Result"
}
],
"connections": {
"Tool Input": {
"main": [
[
{
"node": "Validate Status Proposal",
"type": "main",
"index": 0
}
]
]
},
"Validate Status Proposal": {
"main": [
[
{
"node": "Proposal Is Valid?",
"type": "main",
"index": 0
}
]
]
},
"Proposal Is Valid?": {
"main": [
[
{
"node": "Find Proposed Task",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Audit",
"type": "main",
"index": 0
}
]
]
},
"Find Proposed Task": {
"main": [
[
{
"node": "Prepare Status Proposal",
"type": "main",
"index": 0
}
]
]
},
"Prepare Status Proposal": {
"main": [
[
{
"node": "Task Is Ready?",
"type": "main",
"index": 0
}
]
]
},
"Task Is Ready?": {
"main": [
[
{
"node": "Supersede Older Proposals",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Audit",
"type": "main",
"index": 0
}
]
]
},
"Supersede Older Proposals": {
"main": [
[
{
"node": "Insert Pending Action",
"type": "main",
"index": 0
}
]
]
},
"Insert Pending Action": {
"main": [
[
{
"node": "Shape Status Proposal",
"type": "main",
"index": 0
}
]
]
},
"Shape Status Proposal": {
"main": [
[
{
"node": "Prepare Audit",
"type": "main",
"index": 0
}
]
]
},
"Prepare Audit": {
"main": [
[
{
"node": "Write Tool Audit",
"type": "main",
"index": 0
}
]
]
},
"Write Tool Audit": {
"main": [
[
{
"node": "Return Tool Result",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"executionTimeout": 20,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveManualExecutions": true
},
"versionId": "ae5eaa55-5555-4555-8555-555555555555",
"meta": {
"phase": 5,
"testedWithN8n": "2.30.5",
"toolRisk": "write",
"taskMutation": "none",
"confirmation": "required-before-execution"
},
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
31 - TOOL - Propose update_task_status. Uses executeWorkflowTrigger, dataTable. Event-driven trigger; 13 nodes.
Source: https://github.com/drsamdonegan/ai-solopreneur/blob/main/n8n/workflows/31-tool-propose-update-task-status.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.
Reagendamiento. Uses executeWorkflowTrigger, redis, n8n-nodes-evolution-api, dataTable. Event-driven trigger; 73 nodes.
Agendamiento. Uses n8n-nodes-evolution-api, redis, dataTable, executeWorkflowTrigger. Event-driven trigger; 60 nodes.
Cancelacion. Uses executeWorkflowTrigger, redis, n8n-nodes-evolution-api, dataTable. Event-driven trigger; 36 nodes.
40 - CONFIRM - Task Write. Uses executeWorkflowTrigger, dataTable. Event-driven trigger; 19 nodes.
This workflow provides a reusable error handling, audit logging, and observability pattern for n8n workflows using two n8n custom Data Tables: and .