fix: auto-inject webhookId on webhook nodes during create/update (#643) (#657)

n8n 2.10+ requires webhookId (UUID) on webhook-type nodes for proper
webhook URL registration. Without it, webhooks silently fail with 404.
The n8n UI always generates webhookId but programmatic creation via
n8n-mcp did not.

Add ensureWebhookIds() helper that injects crypto.randomUUID() on
webhook, webhookTrigger, formTrigger, and chatTrigger nodes when
webhookId is missing. Called from both cleanWorkflowForCreate() and
cleanWorkflowForUpdate(). Existing webhookId values are preserved.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2026-03-22 23:20:34 +01:00
committed by GitHub
parent 93816fce30
commit 1f0738e637
8 changed files with 139 additions and 5 deletions

View File

@@ -1,3 +1,4 @@
import crypto from 'crypto';
import { z } from 'zod';
import { WorkflowNode, WorkflowConnection, Workflow } from '../types/n8n-api';
import { isTriggerNode, isActivatableTrigger } from '../utils/node-type-utils';
@@ -87,6 +88,22 @@ export function validateWorkflowSettings(settings: unknown): z.infer<typeof work
return workflowSettingsSchema.parse(settings);
}
const WEBHOOK_NODE_TYPES = new Set([
'n8n-nodes-base.webhook',
'n8n-nodes-base.webhookTrigger',
'n8n-nodes-base.formTrigger',
'@n8n/n8n-nodes-langchain.chatTrigger',
]);
function ensureWebhookIds(nodes?: WorkflowNode[]): void {
if (!nodes) return;
for (const node of nodes) {
if (WEBHOOK_NODE_TYPES.has(node.type) && !node.webhookId) {
node.webhookId = crypto.randomUUID();
}
}
}
// Clean workflow data for API operations
export function cleanWorkflowForCreate(workflow: Partial<Workflow>): Partial<Workflow> {
const {
@@ -109,6 +126,8 @@ export function cleanWorkflowForCreate(workflow: Partial<Workflow>): Partial<Wor
cleanedWorkflow.settings = defaultWorkflowSettings;
}
ensureWebhookIds(cleanedWorkflow.nodes);
return cleanedWorkflow;
}
@@ -194,6 +213,8 @@ export function cleanWorkflowForUpdate(workflow: Workflow): Partial<Workflow> {
cleanedWorkflow.settings = { executionOrder: 'v1' as const };
}
ensureWebhookIds(cleanedWorkflow.nodes);
return cleanedWorkflow;
}