n8n-nodes-corp-api
v1.0.1
Published
n8n Community Nodes for Corp-API — WhatsApp Multi-Session + VoIP automation
Maintainers
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
- Pré-requisitos
- Estrutura do Projeto
- Step-by-Step — Criação
- Credential (Autenticação)
- Nodes da API
- Webhook Trigger Node
- Build & Teste Local
- Publicar no npm
- 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 -y3.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.nodesen8n.credentialssã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:
- Adicione o node "Corp-API Trigger" ao workflow
- Ele gera uma URL de webhook (ex:
https://n8n.meu-dominio.com/webhook/abc123)- Use o endpoint
POST /api/v1/webhooksda Corp-API para registrar essa URL- 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 buildIsso 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 startOpçã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 startOpçã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/n8nDepois, 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 publicApós a publicação, o pacote estará disponível em:
https://www.npmjs.com/package/n8n-nodes-corp-api9. Instalar no n8n
9.1 Via Community Nodes (interface do n8n)
- Acesse Settings → Community Nodes
- Clique em Install a Community Node
- Digite o nome do pacote:
n8n-nodes-corp-api - Marque I understand the risks... e clique Install
- 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
- n8n Documentation — Creating Nodes
- n8n Community Nodes — Requirements
- Corp-API Repository
- n8n Workflow Package on npm
📦 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"