first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.formIoTrigger",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Communication"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/formiotrigger/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.formiotrigger/"
}
]
}
}
@@ -0,0 +1,212 @@
import {
type IDataObject,
type IHookFunctions,
type IWebhookFunctions,
type ILoadOptionsFunctions,
type INodePropertyOptions,
type INodeType,
type INodeTypeDescription,
type IWebhookResponseData,
NodeConnectionTypes,
} from 'n8n-workflow';
import { formIoApiRequest } from './GenericFunctions';
export class FormIoTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Form.io Trigger',
name: 'formIoTrigger',
icon: 'file:formio.svg',
group: ['trigger'],
version: 1,
subtitle: '={{$parameter["event"]}}',
description: 'Handle form.io events via webhooks',
defaults: {
name: 'Form.io Trigger',
},
inputs: [],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'formIoApi',
required: true,
},
],
webhooks: [
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
{
displayName: 'Project Name or ID',
name: 'projectId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getProjects',
},
required: true,
default: '',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
},
{
displayName: 'Form Name or ID',
name: 'formId',
type: 'options',
typeOptions: {
loadOptionsDependsOn: ['projectId'],
loadOptionsMethod: 'getForms',
},
required: true,
default: '',
description:
'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
},
{
displayName: 'Trigger Events',
name: 'events',
type: 'multiOptions',
options: [
{
name: 'Submission Created',
value: 'create',
},
{
name: 'Submission Updated',
value: 'update',
},
],
required: true,
default: [],
},
{
displayName: 'Simplify',
name: 'simple',
type: 'boolean',
default: true,
description:
'Whether to return a simplified version of the response instead of the raw data',
},
],
};
methods = {
loadOptions: {
async getProjects(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const projects = await formIoApiRequest.call(this, 'GET', '/project', {});
const returnData: INodePropertyOptions[] = [];
for (const project of projects) {
returnData.push({
name: project.title,
value: project._id,
});
}
return returnData;
},
async getForms(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const projectId = this.getCurrentNodeParameter('projectId') as string;
const forms = await formIoApiRequest.call(this, 'GET', `/project/${projectId}/form`, {});
const returnData: INodePropertyOptions[] = [];
for (const form of forms) {
returnData.push({
name: form.title,
value: form._id,
});
}
return returnData;
},
},
};
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
const webhookUrl = this.getNodeWebhookUrl('default');
const formId = this.getNodeParameter('formId') as string;
const projectId = this.getNodeParameter('projectId') as string;
const method = this.getNodeParameter('events') as string[];
const actions = await formIoApiRequest.call(
this,
'GET',
`/project/${projectId}/form/${formId}/action`,
);
for (const action of actions) {
if (action.name === 'webhook') {
if (
action.settings.url === webhookUrl &&
action.method.length === method.length &&
action.method.every((value: any) => method.includes(value as string))
) {
webhookData.webhookId = action._id;
return true;
}
}
}
return false;
},
async create(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
const formId = this.getNodeParameter('formId') as string;
const projectId = this.getNodeParameter('projectId') as string;
const webhookUrl = this.getNodeWebhookUrl('default') as string;
const method = this.getNodeParameter('events') as string[];
const payload = {
data: {
name: 'webhook',
title: `webhook-n8n:${webhookUrl}`,
method,
handler: ['after'],
priority: 0,
settings: {
method: 'post',
block: false,
url: webhookUrl,
},
condition: {
field: 'submit',
},
},
};
const webhook = await formIoApiRequest.call(
this,
'POST',
`/project/${projectId}/form/${formId}/action`,
payload,
);
webhookData.webhookId = webhook._id;
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node');
const formId = this.getNodeParameter('formId') as string;
const projectId = this.getNodeParameter('projectId') as string;
await formIoApiRequest.call(
this,
'DELETE',
`/project/${projectId}/form/${formId}/action/${webhookData.webhookId}`,
);
delete webhookData.webhookId;
return true;
},
},
};
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const req = this.getRequestObject();
const simple = this.getNodeParameter('simple') as boolean;
let response = req.body.request;
if (simple) {
response = response.data;
}
return {
workflowData: [this.helpers.returnJsonArray(response as IDataObject[])],
};
}
}
@@ -0,0 +1,48 @@
import type {
IHookFunctions,
IHttpRequestMethods,
IHttpRequestOptions,
ILoadOptionsFunctions,
IWebhookFunctions,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
interface IFormIoCredentials {
environment: 'cloudHosted' | ' selfHosted';
domain?: string;
email: string;
password: string;
}
/**
* Method will call register or list webhooks based on the passed method in the parameter
*/
export async function formIoApiRequest(
this: IHookFunctions | ILoadOptionsFunctions | IWebhookFunctions,
method: IHttpRequestMethods,
endpoint: string,
body = {},
qs = {},
): Promise<any> {
const credentials = await this.getCredentials<IFormIoCredentials>('formIoApi');
const base = credentials.domain || 'https://api.form.io';
const options: IHttpRequestOptions = {
headers: {
'Content-Type': 'application/json',
},
method,
body,
qs,
url: `${base}${endpoint}`,
json: true,
};
try {
return await this.helpers.httpRequestWithAuthentication.call(this, 'formIoApi', options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-name="Layer 1" viewBox="0 0 48 48"><image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAC3FBMVEUAAAAQdbwPdbwPdbwPdbxUsUdUskdUskdUskdVskgQdrwPdbwPdbwPdbwPdbwQdbxUskdUskdUskdUskdVskhVskgUdroQdbwPdbwPdbwPdbwPdbxUskdUskdUskcQdrwPdbwPdbwPdbxUskdUskdUskdUskdUskdWs0kQdrwQdbwPdbwPdbwPdbwQdbxWs0lUskdUskdUskdUskdVskgVeL0RdrwPdbwPdbwPdbwPdbwPdbwQdbxUskdUskdUskdUskdUskdVskkRdrwPdbwPdbwPdbwPdbwPdbwQdbxYskxUskdUskdUskdUskdUskdXs0oSdrwPdbwPdbwPdbwPdbwPdbwQdbwbe75VsklUskdUskdUskdUskdVskgRdrwPdbwPdbwPdbwPdbwQdbxUskdUskdUskdVskhWskkRdrwPdbwPdbwPdbwPdbwQdbxWsklUskdUskdUskdUskdUskcPdbwPdbwPdbwPdbwUd71VskhUskdUskdUskdVskgPdbwPdbwPdbwUeL1VskhUskdUskdUskdUskcPdbwPdbwPdbwQdbxVsklUskdUskdUskdUskdUskdVskgPdbwPdbwPdbwPdbwPdbxUskdUskdUskdUskdUskdUskdUskgTd7wQdbwPdbwPdbwPdbwUeL1VskhUskdUskdVskgPdbwPdbwPdbwPdbwQdbxVs0hUskdUskdUskdUskdUskcQdbwPdbwPdbwPdbwRdrxUskhUskdUskdUskdVskgWeb4QdbwPdbwPdbwPdbwPdbxVskhUskdUskdUskdVskgRdrwPdbwPdbwPdbxUskdUskdUskdUskdWskkPdbwPdbwPdbwPdbxUskdUskdUskdUskcSdr0PdbwPdbwPdbwQdbxUskdUskcQdbwPdbwPdbwPdbwPdbwXeL5UskdUskdUskdVskkQdbwPdbwRdrxUskdUskcPdbxUskf///9ORprqAAAA8XRSTlMAGmZpGwE4d0kKCErK/KkQOt3znicBATin8Oou/n8ZE3Lisz7l/b9HBQZBuvalIgFMz++NIgEflO/+wVAMHXjk2WEMEHDV/eF9GwE5pvW2RAQFQbX6+LA1AQlg1uuKIR2M7M9cDiKI01wKA2DS6YQhBUCv+rAwL+S2QwIPbdipCE/jTgIOifvRFBq+kx4CQ7v4+X0CIefOaxUBL5Lo/M0LBDq9uDcDDGLeGhPY5oEXAjT3sj8HKY7JYgsii8xnEAM8t/ekOA1dy+wcEW9hAhOdwUIEMpnz22PbbxgHVsPZH17xFoLe3GUBoMpRBgYdBCEWUIfmiQAAAAFiS0dE86yxvu4AAAAHdElNRQflBgsXNAUANeC9AAAB60lEQVRIx2NgGAWjgFaAkYmZBcxgZWPn4CSonIubh/cjHz+IKSD4SUhYRBSvcjFxCcmPHz9KSYM47J8+fZIRlJXDo15eQfEjCCiBbVBWAer4pKqmroFDuaaWNi9IuY6uHpivb2CoCtJiZGyCzV2mZuYWIOWWVtY2MDFbO3uQDhkHRycM9c4urm4g9e4enkiiXt4+viAtvn7+ASjKA4OCQ0DKQ8PCI1ANioyKlgFpiYmNQxKNT0gEuyYpOQXTralpMWB3pWdkQkWysnPArsnNy8caGAWFRcVgd5WUQgTKysGuqaiswhXc1TW1YHfV1YO5DWDnNzY1446gltY2kIb2DjCvswscW1LdPTiU9/b1TwCpnzhpMkRgSsJUkA63adNnYFE+c9bsOSDlc+c5zoeJLVi4COws3sVLlqKrX7Z8xSdIfK9EFl7FtBrsrjVr16EoX7+haCNIefGmzVvQTNq6Dewu3u07diIEd+2GpI09e/dhunX/gYNgdx06fAQaXkePHQe75kTsSeyhcep0OcRdZ86CuOfOg8Nm7oWLl3AF99nLV66CdFwDp+/r4LC/cfMWAx5w+85dYJ64dx/EfjD306eH50sZCIBHj588ffYc7KQX9i9fvSaknoHhzdu376Cefv9BlLD6UTAK6AUAHXsSvNupchsAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDYtMTFUMjM6NTI6MDQrMDA6MDBy76P8AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTA2LTExVDIzOjUyOjA0KzAwOjAwA7IbQAAAAABJRU5ErkJggg==" width="48" height="48"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB