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,51 @@
import type {
IDataObject,
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
JsonObject,
IRequestOptions,
IHttpRequestMethods,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';
export async function humanticAiApiRequest(
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
method: IHttpRequestMethods,
resource: string,
body: any = {},
qs: IDataObject = {},
option: IDataObject = {},
): Promise<any> {
try {
const credentials = await this.getCredentials('humanticAiApi');
let options = {
headers: {
'Content-Type': 'application/json',
},
method,
qs,
body,
uri: `https://api.humantic.ai/v1${resource}`,
json: true,
} satisfies IRequestOptions;
options = Object.assign({}, options, option);
options.qs.apikey = credentials.apiKey;
if (Object.keys(options.body as IDataObject).length === 0) {
delete options.body;
}
const response = await this.helpers.request(options);
if (response.data && response.data.status === 'error') {
throw new NodeApiError(this.getNode(), response.data as JsonObject);
}
return response;
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
@@ -0,0 +1,25 @@
{
"node": "n8n-nodes-base.humanticAi",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Analytics"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/humanticai/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.humanticai/"
}
],
"generic": [
{
"label": "5 tasks you can automate with the new Notion API ",
"icon": "⚡️",
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
}
]
}
}
@@ -0,0 +1,176 @@
import type {
IDataObject,
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';
import { humanticAiApiRequest } from './GenericFunctions';
import { profileFields, profileOperations } from './ProfileDescription';
export class HumanticAi implements INodeType {
description: INodeTypeDescription = {
displayName: 'Humantic AI',
name: 'humanticAi',
icon: 'file:humanticai.svg',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Humantic AI API',
defaults: {
name: 'Humantic AI',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'humanticAiApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Profile',
value: 'profile',
},
],
default: 'profile',
},
// PROFILE
...profileOperations,
...profileFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length;
const qs: IDataObject = {};
let responseData;
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < length; i++) {
if (resource === 'profile') {
if (operation === 'create') {
const userId = this.getNodeParameter('userId', i) as string;
const sendResume = this.getNodeParameter('sendResume', i) as boolean;
qs.userid = userId;
if (sendResume) {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
responseData = await humanticAiApiRequest.call(
this,
'POST',
'/user-profile/create',
{},
qs,
{
formData: {
resume: {
value: binaryDataBuffer,
options: {
filename: binaryData.fileName,
},
},
},
},
);
} else {
responseData = await humanticAiApiRequest.call(
this,
'GET',
'/user-profile/create',
{},
qs,
);
}
if (responseData.data !== undefined) {
responseData = responseData.data;
} else {
delete responseData.usage_stats;
}
}
if (operation === 'get') {
const userId = this.getNodeParameter('userId', i) as string;
const options = this.getNodeParameter('options', i);
qs.userid = userId;
if (options.persona) {
qs.persona = (options.persona as string[]).join(',');
}
responseData = await humanticAiApiRequest.call(this, 'GET', '/user-profile', {}, qs);
responseData = responseData.results;
}
if (operation === 'update') {
const userId = this.getNodeParameter('userId', i) as string;
const sendResume = this.getNodeParameter('sendResume', i) as string;
qs.userid = userId;
if (sendResume) {
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
responseData = await humanticAiApiRequest.call(
this,
'POST',
'/user-profile/create',
{},
qs,
{
formData: {
resume: {
value: binaryDataBuffer,
options: {
filename: binaryData.fileName,
},
},
},
},
);
responseData = responseData.data;
} else {
const text = this.getNodeParameter('text', i) as string;
const body: IDataObject = {
text,
};
qs.userid = userId;
responseData = await humanticAiApiRequest.call(
this,
'POST',
'/user-profile/create',
body,
qs,
);
responseData = responseData.data;
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}
@@ -0,0 +1,196 @@
import type { INodeProperties } from 'n8n-workflow';
export const profileOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['profile'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a profile',
action: 'Create a profile',
},
{
name: 'Get',
value: 'get',
description: 'Retrieve a profile',
action: 'Get a profile',
},
{
name: 'Update',
value: 'update',
description: 'Update a profile',
action: 'Update a profile',
},
],
default: 'create',
},
];
export const profileFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* profile:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'User ID',
name: 'userId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['create'],
resource: ['profile'],
},
},
description:
'The LinkedIn profile URL or email ID for creating a Humantic profile. If you are sending the resume, this should be a unique string.',
},
{
displayName: 'Send Resume',
name: 'sendResume',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['create'],
resource: ['profile'],
},
},
description: 'Whether to send a resume for a resume based analysis',
},
{
displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
displayOptions: {
show: {
operation: ['create'],
resource: ['profile'],
sendResume: [true],
},
},
hint: 'The name of the input binary field containing the resume in PDF or DOCX format',
},
/* -------------------------------------------------------------------------- */
/* profile:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'User ID',
name: 'userId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['get'],
resource: ['profile'],
},
},
description:
'This value is the same as the User ID that was provided when the analysis was created. This could be a LinkedIn URL, email ID, or a unique string in case of resume based analysis.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['get'],
resource: ['profile'],
},
},
options: [
{
displayName: 'Persona',
name: 'persona',
type: 'multiOptions',
options: [
{
name: 'Sales',
value: 'sales',
},
{
name: 'Hiring',
value: 'hiring',
},
],
default: [],
description:
'Fetch the Humantic profile of the user for a particular persona type. Multiple persona values can be supported using comma as a delimiter.',
},
],
},
/* -------------------------------------------------------------------------- */
/* profile:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'User ID',
name: 'userId',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
operation: ['update'],
resource: ['profile'],
},
},
description:
'This value is the same as the User ID that was provided when the analysis was created. Currently only supported for profiles created using LinkedIn URL.',
},
{
displayName: 'Send Resume',
name: 'sendResume',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: ['update'],
resource: ['profile'],
},
},
description: 'Whether to send a resume for a resume of the user',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
default: '',
displayOptions: {
show: {
operation: ['update'],
resource: ['profile'],
sendResume: [false],
},
},
description: 'Additional text written by the user',
},
{
displayName: 'Input Binary Field',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
displayOptions: {
show: {
operation: ['update'],
resource: ['profile'],
sendResume: [true],
},
},
hint: 'The name of the input binary field containing the resume in PDF or DOCX format',
},
];
@@ -0,0 +1,6 @@
<svg width="40" height="41" viewBox="0 0 40 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M36.4722 30.9096C38.2943 27.927 39.3443 24.4213 39.3443 20.6703C39.3443 9.80671 30.5367 1 19.6721 1C8.80751 1 0 9.80671 0 20.6703C0 31.534 8.80751 40.3407 19.6721 40.3407C23.9509 40.3407 27.9106 38.9748 31.1389 36.6552L40 40.0128L36.4722 30.9096Z" fill="#FEBD00"/>
<path d="M33.214 28.9076C34.6756 26.5081 35.5179 23.6879 35.5179 20.6704C35.5179 11.9309 28.4526 4.84619 19.737 4.84619C11.0214 4.84619 3.95605 11.9309 3.95605 20.6704C3.95605 29.4098 11.0214 36.4945 19.737 36.4945C23.1694 36.4945 26.3459 35.3957 28.9356 33.5297L36.044 36.2308L33.214 28.9076Z" fill="#FFCD3A"/>
<path d="M29.1409 26.3907C30.1521 24.7244 30.7348 22.7659 30.7348 20.6704C30.7348 14.6013 25.8471 9.6814 19.8178 9.6814C13.7886 9.6814 8.90088 14.6013 8.90088 20.6704C8.90088 26.7395 13.7886 31.6594 19.8178 31.6594C22.1923 31.6594 24.3897 30.8963 26.1813 29.6005L31.0987 31.4763L29.1409 26.3907Z" fill="#FFD96B"/>
<path d="M25.5211 24.1025C26.1318 23.1027 26.4837 21.9276 26.4837 20.6703C26.4837 17.0289 23.5317 14.0769 19.8903 14.0769C16.2488 14.0769 13.2969 17.0289 13.2969 20.6703C13.2969 24.3117 16.2488 27.2637 19.8903 27.2637C21.3244 27.2637 22.6515 26.8059 23.7335 26.0284L26.7035 27.1538L25.5211 24.1025Z" fill="#FFF4D3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB