npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

n8n-nodes-corp-api

v1.0.1

Published

n8n Community Nodes for Corp-API — WhatsApp Multi-Session + VoIP automation

Readme

📦 Guia: Criar Community Node n8n para Corp-API

Este guia explica passo a passo como criar um n8n Community Node que expõe todos os endpoints da Corp-API como nodes visuais no n8n.


📋 Sumário

  1. Pré-requisitos
  2. Estrutura do Projeto
  3. Step-by-Step — Criação
  4. Credential (Autenticação)
  5. Nodes da API
  6. Webhook Trigger Node
  7. Build & Teste Local
  8. Publicar no npm
  9. Instalar no n8n

1. Pré-requisitos

# Node.js 18+
node --version

# n8n instalado globalmente (para teste)
npm install -g n8n

# Conta no npmjs.com (para publicar o pacote)

2. Estrutura do Projeto

Crie o projeto fora do repositório corp-api — é um pacote npm independente.

n8n-nodes-corp-api/
├── .gitignore
├── .npmignore
├── package.json
├── tsconfig.json
├── README.md
├── credentials/
│   └── CorpApi.credentials.ts    # Autenticação MASTER_TOKEN
├── nodes/
│   ├── CorpApiTrigger/
│   │   ├── CorpApiTrigger.node.ts    # Webhook Trigger (recebe eventos)
│   │   └── corp-api-logo.svg
│   ├── SendMessage/
│   │   └── SendMessage.node.ts       # Enviar texto/mídia/localização
│   ├── ManageInstance/
│   │   └── ManageInstance.node.ts    # Conectar/desconectar/status/QR
│   ├── ManageGroup/
│   │   └── ManageGroup.node.ts       # Listar/atualizar grupos
│   └── VoipCall/
│       └── VoipCall.node.ts          # Chamadas VoIP
└── utils/
    └── api.ts                        # Helper HTTP (chamadas à Corp-API)

3. Step-by-Step

3.1 Criar o projeto

mkdir n8n-nodes-corp-api
cd n8n-nodes-corp-api
npm init -y

3.2 package.json

{
  "name": "n8n-nodes-corp-api",
  "version": "1.0.0",
  "description": "n8n Community Nodes for Corp-API — WhatsApp Multi-Session + VoIP automation",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "prepublishOnly": "npm run build"
  },
  "files": [
    "dist/"
  ],
  "n8n": {
    "n8nNodesApiVersion": 1,
    "nodes": [
      "dist/nodes/CorpApiTrigger/CorpApiTrigger.node.js",
      "dist/nodes/SendMessage/SendMessage.node.js",
      "dist/nodes/ManageInstance/ManageInstance.node.js",
      "dist/nodes/ManageGroup/ManageGroup.node.js",
      "dist/nodes/VoipCall/VoipCall.node.js"
    ],
    "credentials": [
      "dist/credentials/CorpApi.credentials.js"
    ]
  },
  "dependencies": {
    "n8n-workflow": "^1.50.0"
  },
  "devDependencies": {
    "typescript": "^5.6.0",
    "@types/node": "^22.0.0"
  },
  "keywords": ["n8n-community-node-package", "whatsapp", "voip", "corp-api", "zapo"],
  "license": "MIT"
}

IMPORTANTE: O campo n8n.nodes e n8n.credentials são obrigatórios — é assim que o n8n descobre seus nodes.

3.3 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "commonjs",
    "lib": ["ES2021"],
    "outDir": "./dist",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "resolveJsonModule": true
  },
  "include": ["credentials/**/*.ts", "nodes/**/*.ts", "utils/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

4. Credential

Arquivo: credentials/CorpApi.credentials.ts

import {
  IAuthenticateGeneric,
  ICredentialTestRequest,
  ICredentialType,
  INodeProperties,
} from 'n8n-workflow';

export class CorpApi implements ICredentialType {
  name = 'corpApi';
  displayName = 'Corp-API (WhatsApp)';
  documentationUrl = 'https://github.com/kabaweb/corp-api';
  
  properties: INodeProperties[] = [
    {
      displayName: 'API Base URL',
      name: 'baseUrl',
      type: 'string',
      default: 'http://localhost:3000/api/v1',
      description: 'URL da sua Corp-API (ex: https://meu-dominio.com/api/v1)',
      required: true,
    },
    {
      displayName: 'Master Token',
      name: 'masterToken',
      type: 'string',
      typeOptions: { password: true },
      default: '',
      description: 'Token mestre configurado no MASTER_TOKEN do docker-compose.yml',
      required: true,
    },
  ];

  // O n8n usa isso para popular o header Authorization automaticamente
  authenticate: IAuthenticateGeneric = {
    type: 'generic',
    properties: {
      headers: {
        Authorization: '=Bearer {{$credentials.masterToken}}',
      },
    },
  };

  // Teste de conexão: chama /health
  test: ICredentialTestRequest = {
    request: {
      baseURL: '={{$credentials.baseUrl.replace(/\/api\/v1$/, "")}}',
      url: '/health',
      method: 'GET',
    },
  };
}

5. Nodes da API

5.1 Helper HTTP compartilhado

Arquivo: utils/api.ts

import { IExecuteFunctions, IHttpRequestOptions } from 'n8n-workflow';

export async function corpApiRequest(
  thisAction: IExecuteFunctions,
  method: 'GET' | 'POST' | 'PUT' | 'DELETE',
  endpoint: string,
  body?: Record<string, unknown>,
): Promise<any> {
  const credentials = await thisAction.getCredentials('corpApi');
  const baseUrl = (credentials.baseUrl as string).replace(/\/$/, '');

  const options: IHttpRequestOptions = {
    method,
    url: `${baseUrl}${endpoint}`,
    headers: {
      Authorization: `Bearer ${credentials.masterToken}`,
      'Content-Type': 'application/json',
    },
    json: true,
  };

  if (body) {
    options.body = body;
  }

  return thisAction.helpers.httpRequest(options);
}

5.2 Node: SendMessage

Arquivo: nodes/SendMessage/SendMessage.node.ts

import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';
import { corpApiRequest } from '../../utils/api';

export class SendMessage implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Corp-API Send Message',
    name: 'sendMessage',
    icon: 'fa:paper-plane',
    group: ['transform'],
    version: 1,
    description: 'Envia mensagens via WhatsApp (texto, mídia, localização, reação)',
    defaults: { name: 'Send WhatsApp Message' },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [{ name: 'corpApi', required: true }],
    properties: [
      {
        displayName: 'Instance ID',
        name: 'instanceId',
        type: 'string',
        default: '',
        required: true,
        description: 'ID da instância configurada na Corp-API',
      },
      {
        displayName: 'Message Type',
        name: 'messageType',
        type: 'options',
        options: [
          { name: 'Text', value: 'text' },
          { name: 'Media (Image/Video/Document)', value: 'media' },
          { name: 'Location', value: 'location' },
          { name: 'Reaction', value: 'reaction' },
        ],
        default: 'text',
        required: true,
      },
      {
        displayName: 'To (Phone Number)',
        name: 'to',
        type: 'string',
        default: '',
        required: true,
        description: 'Número do destinatário (ex: 5581999999999)',
        displayOptions: { show: { messageType: ['text', 'media', 'location', 'reaction'] } },
      },
      // --- Text ---
      {
        displayName: 'Text',
        name: 'text',
        type: 'string',
        typeOptions: { rows: 3 },
        default: '',
        required: true,
        displayOptions: { show: { messageType: ['text'] } },
      },
      // --- Media ---
      {
        displayName: 'Media Type',
        name: 'mediaType',
        type: 'options',
        options: [
          { name: 'Image', value: 'image' },
          { name: 'Video', value: 'video' },
          { name: 'Document', value: 'document' },
        ],
        default: 'image',
        displayOptions: { show: { messageType: ['media'] } },
      },
      {
        displayName: 'Media Base64',
        name: 'base64',
        type: 'string',
        default: '',
        required: true,
        description: 'Arquivo em Base64',
        displayOptions: { show: { messageType: ['media'] } },
      },
      // --- Location ---
      {
        displayName: 'Latitude',
        name: 'latitude',
        type: 'number',
        default: 0,
        required: true,
        displayOptions: { show: { messageType: ['location'] } },
      },
      {
        displayName: 'Longitude',
        name: 'longitude',
        type: 'number',
        default: 0,
        required: true,
        displayOptions: { show: { messageType: ['location'] } },
      },
      // --- Reaction ---
      {
        displayName: 'Message ID',
        name: 'messageId',
        type: 'string',
        default: '',
        required: true,
        description: 'ID da mensagem original',
        displayOptions: { show: { messageType: ['reaction'] } },
      },
      {
        displayName: 'Emoji',
        name: 'emoji',
        type: 'string',
        default: '👍',
        required: true,
        displayOptions: { show: { messageType: ['reaction'] } },
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const instanceId = this.getNodeParameter('instanceId', i) as string;
      const messageType = this.getNodeParameter('messageType', i) as string;
      const to = this.getNodeParameter('to', i) as string;

      let endpoint: string;
      let body: Record<string, unknown>;

      switch (messageType) {
        case 'text':
          endpoint = `/${instanceId}/message/send-text`;
          body = { to, text: this.getNodeParameter('text', i) };
          break;
        case 'media':
          endpoint = `/${instanceId}/message/send-media`;
          body = {
            to,
            type: this.getNodeParameter('mediaType', i),
            base64: this.getNodeParameter('base64', i),
          };
          break;
        case 'location':
          endpoint = `/${instanceId}/message/send-location`;
          body = {
            to,
            latitude: this.getNodeParameter('latitude', i),
            longitude: this.getNodeParameter('longitude', i),
          };
          break;
        case 'reaction':
          endpoint = `/${instanceId}/message/react`;
          body = {
            to,
            messageId: this.getNodeParameter('messageId', i),
            emoji: this.getNodeParameter('emoji', i),
          };
          break;
        default:
          throw new Error(`Tipo de mensagem inválido: ${messageType}`);
      }

      const response = await corpApiRequest.call(this, 'POST', endpoint, body);
      returnData.push({ json: response });
    }

    return [returnData];
  }
}

5.3 Node: ManageInstance

Arquivo: nodes/ManageInstance/ManageInstance.node.ts

import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';
import { corpApiRequest } from '../../utils/api';

export class ManageInstance implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Corp-API Manage Instance',
    name: 'manageInstance',
    icon: 'fa:mobile-alt',
    group: ['transform'],
    version: 1,
    description: 'Gerencia instâncias WhatsApp: conectar, desconectar, status, QR code',
    defaults: { name: 'Manage Instance' },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [{ name: 'corpApi', required: true }],
    properties: [
      {
        displayName: 'Operation',
        name: 'operation',
        type: 'options',
        options: [
          { name: 'Create Instance', value: 'create' },
          { name: 'Connect (Generate QR)', value: 'connect' },
          { name: 'Disconnect', value: 'disconnect' },
          { name: 'Restart', value: 'restart' },
          { name: 'Get QR Code', value: 'qrcode' },
          { name: 'Get Status', value: 'status' },
          { name: 'List Instances', value: 'list' },
          { name: 'Delete Instance', value: 'delete' },
        ],
        default: 'status',
        required: true,
      },
      {
        displayName: 'Instance ID',
        name: 'instanceId',
        type: 'string',
        default: '',
        required: true,
        description: 'ID da instância',
        displayOptions: { show: { operation: ['connect', 'disconnect', 'restart', 'qrcode', 'status', 'delete'] } },
      },
      {
        displayName: 'Instance ID',
        name: 'instanceIdCreate',
        type: 'string',
        default: '',
        required: true,
        description: 'ID único da nova instância',
        displayOptions: { show: { operation: ['create'] } },
      },
      {
        displayName: 'Instance Name',
        name: 'name',
        type: 'string',
        default: '',
        required: true,
        description: 'Nome descritivo da instância',
        displayOptions: { show: { operation: ['create'] } },
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const operation = this.getNodeParameter('operation', i) as string;
      let endpoint: string;
      let method: 'GET' | 'POST' | 'DELETE' = 'POST';
      let body: Record<string, unknown> | undefined;

      switch (operation) {
        case 'create':
          endpoint = '/instances';
          body = {
            id: this.getNodeParameter('instanceIdCreate', i),
            name: this.getNodeParameter('name', i),
          };
          break;
        case 'list':
          endpoint = '/instances';
          method = 'GET';
          break;
        case 'delete':
          endpoint = `/instances/${this.getNodeParameter('instanceId', i)}`;
          method = 'DELETE';
          break;
        case 'connect':
          endpoint = `/${this.getNodeParameter('instanceId', i)}/connect`;
          break;
        case 'disconnect':
          endpoint = `/${this.getNodeParameter('instanceId', i)}/disconnect`;
          break;
        case 'restart':
          endpoint = `/${this.getNodeParameter('instanceId', i)}/restart`;
          break;
        case 'qrcode':
          endpoint = `/${this.getNodeParameter('instanceId', i)}/qrcode`;
          method = 'GET';
          break;
        case 'status':
          endpoint = `/${this.getNodeParameter('instanceId', i)}/status`;
          method = 'GET';
          break;
        default:
          throw new Error(`Operação inválida: ${operation}`);
      }

      const response = await corpApiRequest.call(this, method, endpoint, body);
      returnData.push({ json: response });
    }

    return [returnData];
  }
}

5.4 Node: VoipCall

Arquivo: nodes/VoipCall/VoipCall.node.ts

import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';
import { corpApiRequest } from '../../utils/api';

export class VoipCall implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Corp-API VoIP Call',
    name: 'voipCall',
    icon: 'fa:phone',
    group: ['transform'],
    version: 1,
    description: 'Gerencia chamadas VoIP via WhatsApp',
    defaults: { name: 'VoIP Call' },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [{ name: 'corpApi', required: true }],
    properties: [
      {
        displayName: 'Operation',
        name: 'operation',
        type: 'options',
        options: [
          { name: 'Make Call', value: 'call' },
          { name: 'Hang Up', value: 'hangup' },
          { name: 'Accept Call', value: 'accept' },
          { name: 'Reject Call', value: 'reject' },
          { name: 'Mute', value: 'mute' },
          { name: 'Unmute', value: 'unmute' },
          { name: 'Hold', value: 'hold' },
          { name: 'Resume', value: 'resume' },
          { name: 'List Active Calls', value: 'listCalls' },
          { name: 'Get Call Details', value: 'getCall' },
          { name: 'List Recordings', value: 'listRecordings' },
          { name: 'Get Recording', value: 'getRecording' },
        ],
        default: 'call',
        required: true,
      },
      {
        displayName: 'Instance ID',
        name: 'instanceId',
        type: 'string',
        default: '',
        required: true,
      },
      {
        displayName: 'Phone Number',
        name: 'to',
        type: 'string',
        default: '',
        required: true,
        description: 'Número para ligar (ex: 5581999999999)',
        displayOptions: { show: { operation: ['call'] } },
      },
      {
        displayName: 'Audio (Base64 WAV)',
        name: 'audioBase64',
        type: 'string',
        default: '',
        description: 'Áudio WAV/PCM em Base64 para tocar na chamada (opcional)',
        displayOptions: { show: { operation: ['call'] } },
      },
      {
        displayName: 'Call ID',
        name: 'callId',
        type: 'string',
        default: '',
        required: true,
        description: 'ID da chamada',
        displayOptions: { show: { operation: ['hangup', 'accept', 'reject', 'mute', 'unmute', 'hold', 'resume', 'getCall', 'getRecording'] } },
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const instanceId = this.getNodeParameter('instanceId', i) as string;
      const operation = this.getNodeParameter('operation', i) as string;
      let endpoint: string;
      let method: 'GET' | 'POST' = 'POST';
      let body: Record<string, unknown> | undefined;

      switch (operation) {
        case 'call':
          endpoint = `/${instanceId}/voip/call`;
          body = { to: this.getNodeParameter('to', i) };
          const audio = this.getNodeParameter('audioBase64', i, '') as string;
          if (audio) body.audioBase64 = audio;
          break;
        case 'hangup':
          endpoint = `/${instanceId}/voip/hangup`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'accept':
          endpoint = `/${instanceId}/voip/accept`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'reject':
          endpoint = `/${instanceId}/voip/reject`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'mute':
          endpoint = `/${instanceId}/voip/mute`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'unmute':
          endpoint = `/${instanceId}/voip/unmute`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'hold':
          endpoint = `/${instanceId}/voip/hold`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'resume':
          endpoint = `/${instanceId}/voip/resume`;
          body = { callId: this.getNodeParameter('callId', i) };
          break;
        case 'listCalls':
          endpoint = `/${instanceId}/voip/calls`;
          method = 'GET';
          break;
        case 'getCall':
          endpoint = `/${instanceId}/voip/calls/${this.getNodeParameter('callId', i)}`;
          method = 'GET';
          break;
        case 'listRecordings':
          endpoint = `/${instanceId}/voip/recordings`;
          method = 'GET';
          break;
        case 'getRecording':
          endpoint = `/${instanceId}/voip/recordings/${this.getNodeParameter('callId', i)}`;
          method = 'GET';
          break;
        default:
          throw new Error(`Operação inválida: ${operation}`);
      }

      const response = await corpApiRequest.call(this, method, endpoint, body);
      returnData.push({ json: response });
    }

    return [returnData];
  }
}

5.5 Node: ManageGroup

Arquivo: nodes/ManageGroup/ManageGroup.node.ts

import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';
import { corpApiRequest } from '../../utils/api';

export class ManageGroup implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Corp-API Manage Groups',
    name: 'manageGroup',
    icon: 'fa:users',
    group: ['transform'],
    version: 1,
    description: 'Lista e atualiza grupos do WhatsApp',
    defaults: { name: 'Manage Groups' },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [{ name: 'corpApi', required: true }],
    properties: [
      {
        displayName: 'Instance ID',
        name: 'instanceId',
        type: 'string',
        default: '',
        required: true,
      },
      {
        displayName: 'Operation',
        name: 'operation',
        type: 'options',
        options: [
          { name: 'List Groups', value: 'list' },
          { name: 'Refresh Groups', value: 'refresh' },
        ],
        default: 'list',
        required: true,
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];

    for (let i = 0; i < items.length; i++) {
      const instanceId = this.getNodeParameter('instanceId', i) as string;
      const operation = this.getNodeParameter('operation', i) as string;

      let endpoint: string;
      let method: 'GET' | 'POST' = 'GET';

      if (operation === 'refresh') {
        endpoint = `/${instanceId}/groups/refresh`;
        method = 'POST';
      } else {
        endpoint = `/${instanceId}/groups`;
      }

      const response = await corpApiRequest.call(this, method, endpoint);
      returnData.push({ json: response });
    }

    return [returnData];
  }
}

6. Webhook Trigger Node

Arquivo: nodes/CorpApiTrigger/CorpApiTrigger.node.ts

Este é o node que recebe eventos da Corp-API (funciona como trigger no n8n).

import {
  IWebhookFunctions,
  IWebhookResponseData,
  INodeType,
  INodeTypeDescription,
  IWebhookDescription,
} from 'n8n-workflow';

export class CorpApiTrigger implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Corp-API Trigger',
    name: 'corpApiTrigger',
    icon: 'fa:bolt',
    group: ['trigger'],
    version: 1,
    description: 'Recebe eventos de webhook da Corp-API (mensagens, chamadas, conexões etc.)',
    defaults: { name: 'Corp-API Trigger' },
    inputs: [],
    outputs: ['main'],
    credentials: [{ name: 'corpApi', required: true }],
    webhooks: [
      {
        name: 'default',
        httpMethod: 'POST',
        responseMode: 'onReceived',
        path: 'webhook', // URL: https://<n8n-host>/webhook/<path>
      },
    ],
    properties: [],
  };

  async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
    // O body enviado pela Corp-API:
    // { event: "message", instanceId: "inst_01", timestamp: "...", data: {...} }
    const bodyData = this.getBodyData();
    
    return {
      workflowData: [[{ json: bodyData }]],
    };
  }
}

Como usar no fluxo n8n:

  1. Adicione o node "Corp-API Trigger" ao workflow
  2. Ele gera uma URL de webhook (ex: https://n8n.meu-dominio.com/webhook/abc123)
  3. Use o endpoint POST /api/v1/webhooks da Corp-API para registrar essa URL
  4. Todo evento do WhatsApp será recebido pelo n8n automaticamente

7. Build & Teste Local

7.1 Compilar

cd n8n-nodes-corp-api
npm install
npm run build

Isso gera os arquivos compilados em dist/.

7.2 Testar no n8n local

Opção A — Instalar direto do diretório local:

# Instalar o pacote local no n8n global
npm install -g /caminho/absoluto/n8n-nodes-corp-api

# Rodar n8n
n8n start

Opção B — Link simbólico (desenvolvimento):

cd ~/.n8n
mkdir -p custom
cd custom
npm init -y
npm install /caminho/absoluto/n8n-nodes-corp-api

# Configurar n8n para carregar nodes customizados:
export N8N_CUSTOM_EXTENSIONS=~/.n8n/custom

n8n start

Opção C — Docker (recomendado para produção):

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  -e N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true \
  n8nio/n8n

Depois, no n8n, vá em Settings → Community Nodes → Install e digite n8n-nodes-corp-api.


8. Publicar no npm

cd n8n-nodes-corp-api

# 1. Fazer login no npm
npm login

# 2. Verificar o que será publicado (simulação)
npm pack --dry-run

# 3. Publicar
npm publish --access public

Após a publicação, o pacote estará disponível em:

https://www.npmjs.com/package/n8n-nodes-corp-api

9. Instalar no n8n

9.1 Via Community Nodes (interface do n8n)

  1. Acesse SettingsCommunity Nodes
  2. Clique em Install a Community Node
  3. Digite o nome do pacote: n8n-nodes-corp-api
  4. Marque I understand the risks... e clique Install
  5. Reinicie o n8n

9.2 Via Docker Compose (produção)

# docker-compose.yml do n8n
services:
  n8n:
    image: n8nio/n8n
    ports:
      - "5678:5678"
    environment:
      - N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
      # Pré-instala o pacote no startup
      - N8N_COMMUNITY_PACKAGES_ENABLED=true
    volumes:
      - n8n_data:/home/node/.n8n
    # Instala o pacote no entrypoint
    entrypoint: ["/bin/sh", "-c", "npm install -g n8n-nodes-corp-api && n8n start"]

9.3 Via Kubernetes / Helm

helm upgrade --install n8n n8n/n8n \
  --set env.N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true \
  --set extraPackages[0]="n8n-nodes-corp-api"

🔧 Troubleshooting

| Problema | Solução | |----------|----------| | "Node not found" no n8n | Verifique se o campo n8n.nodes no package.json tem caminhos corretos (relativos ao dist/) | | "Credential not found" | Verifique n8n.credentials no package.json | | Erro de CORS ao chamar a API | Ajuste CORS_ORIGINS no docker-compose da Corp-API para incluir o domínio do n8n | | n8n não puxa nova versão | Clear cache: rm -rf ~/.n8n/cache e reinicie | | Webhook não recebe eventos | Verifique se o endpoint da Corp-API está acessível do n8n (rede/firewall) |


📚 Referências


📦 Resumo Rápido

# 1. Criar projeto
mkdir n8n-nodes-corp-api && cd n8n-nodes-corp-api
npm init -y

# 2. Copie os arquivos deste guia para:
#    credentials/CorpApi.credentials.ts
#    utils/api.ts
#    nodes/SendMessage/SendMessage.node.ts
#    nodes/ManageInstance/ManageInstance.node.ts
#    nodes/VoipCall/VoipCall.node.ts
#    nodes/ManageGroup/ManageGroup.node.ts
#    nodes/CorpApiTrigger/CorpApiTrigger.node.ts

# 3. Build & Publicar
npm install
npm run build
npm login
npm publish --access public

# 4. No n8n:
#    Settings → Community Nodes → Install "n8n-nodes-corp-api"