# Integrações - Krayin CRM v2.1.6
Guia de integração com N8N, WhatsApp (Evolution API) e Frontend Operaes.
📋 Índice
1. N8N Integration
Visão Geral
N8N é uma plataforma de automação que conecta ao Krayin via HTTP Request nodes.
┌─────────────┐ Webhook ┌─────────────┐ API ┌─────────────┐
│ Trigger │ ───────────────► │ N8N │ ───────────► │ Krayin │
│ (WhatsApp) │ │ Workflow │ │ CRM │
└─────────────┘ └─────────────┘ └─────────────┘
Configuração Inicial
Passo 1: Obter Token do Krayin
curl -X POST "https://crm.memudecore.com.br/api/v1/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=admin@memudecore.com.br&password=sua_senha&device_name=N8N"
Salve o token retornado.
Passo 2: Criar Credential no N8N
- Acesse N8N → Settings → Credentials
- Clique Add Credential
- Selecione Header Auth
- Configure:
- Name: Krayin API
- Header Name: Authorization
- Header Value: Bearer {seu_token}
Workflow: Criar Lead de Formulário
┌─────────────────────────────────────────────────────────────────┐
│ N8N WORKFLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [Webhook Trigger] → [Validate Data] → [HTTP Request] → [Done] │
│ │
└─────────────────────────────────────────────────────────────────┘
Node 1: Webhook Trigger
{
"httpMethod": "POST",
"path": "form-to-krayin",
"responseMode": "onReceived"
}
Node 2: Set Node (Transformar Dados)
return {
title: $json.body.empresa || $json.body.nome,
source_id: 1,
lead_type_id: 1,
sales_owner_id: 1,
value: parseFloat($json.body.valor) || 0,
description: $json.body.mensagem || '',
person_data: {
name: $json.body.nome,
email: $json.body.email,
phone: $json.body.telefone
}
}
Node 3: HTTP Request (Criar Contato)
{
"method": "POST",
"url": "https://crm.memudecore.com.br/api/v1/contacts/persons",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"Content-Type": "application/json"
},
"sendBody": true,
"bodyParameters": {
"name": "={{ $json.person_data.name }}",
"emails": [{"value": "={{ $json.person_data.email }}", "label": "work"}],
"contact_numbers": [{"value": "={{ $json.person_data.phone }}", "label": "work"}]
}
}
Node 4: HTTP Request (Criar Lead)
{
"method": "POST",
"url": "https://crm.memudecore.com.br/api/v1/leads",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpHeaderAuth",
"sendBody": true,
"bodyParameters": {
"title": "={{ $('Set').item.json.title }}",
"source_id": 1,
"lead_type_id": 1,
"sales_owner_id": 1,
"value": "={{ $('Set').item.json.value }}",
"contact_person_id": "={{ $json.data.id }}",
"description": "={{ $('Set').item.json.description }}"
}
}
Workflow: Sync Krayin → Google Sheets
// Node: Schedule Trigger (diário às 8h)
// Node: HTTP Request - Buscar leads do dia anterior
{
"method": "GET",
"url": "https://crm.memudecore.com.br/api/v1/leads?created_after={{ $now.minus(1, 'day').toISO() }}"
}
// Node: Google Sheets - Append rows
// Mapear campos do lead para colunas da planilha
2. WhatsApp (Evolution API)
Arquitetura
┌─────────────┐ Mensagem ┌─────────────┐ Webhook ┌─────────────┐
│ WhatsApp │ ───────────────► │ Evolution │ ───────────────► │ N8N │
│ (User) │ │ API │ │ │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
│ API
▼
┌─────────────┐
│ Krayin │
│ CRM │
└─────────────┘
Configurar Evolution API
Passo 1: Criar Instância WhatsApp
curl -X POST "https://evolution.memudecore.com.br/instance/create" \
-H "Authorization: Bearer SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instanceName": "krayin-whatsapp",
"qrcode": true,
"integration": "WHATSAPP-BAILEYS"
}'
Passo 2: Configurar Webhook
curl -X POST "https://evolution.memudecore.com.br/webhook/set/krayin-whatsapp" \
-H "Authorization: Bearer SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"enabled": true,
"url": "https://n8n.memudecore.com.br/webhook/whatsapp-krayin",
"events": [
"MESSAGES_UPSERT",
"CONNECTION_UPDATE"
]
}
}'
Workflow N8N: WhatsApp → Lead
Payload do Evolution API
{
"event": "MESSAGES_UPSERT",
"data": {
"key": {
"remoteJid": "5511987654321@s.whatsapp.net",
"fromMe": false,
"id": "ABC123"
},
"message": {
"conversation": "Olá, tenho interesse no produto X"
},
"pushName": "João Silva"
}
}
Node: Code (Extrair Dados)
const jid = $json.data.key.remoteJid;
const phone = '+' + jid.split('@')[0];
const name = $json.data.pushName || 'WhatsApp Contact';
const message = $json.data.message.conversation ||
$json.data.message.extendedTextMessage?.text ||
'[Mídia]';
return {
phone: phone,
name: name,
message: message,
source: 'whatsapp',
jid: jid
};
Node: HTTP Request (Buscar Contato Existente)
{
"method": "GET",
"url": "https://crm.memudecore.com.br/api/v1/contacts/persons",
"qs": {
"search": "={{ $json.phone }}"
}
}
Node: IF (Contato Existe?)
Condition: {{ $json.data.length > 0 }}
True → Usar ID existente
False → Criar novo contato
Node: HTTP Request (Criar Lead)
{
"method": "POST",
"url": "https://crm.memudecore.com.br/api/v1/leads",
"body": {
"title": "WhatsApp - {{ $('Code').item.json.name }}",
"source_id": 5,
"lead_type_id": 1,
"sales_owner_id": 1,
"contact_person_id": "{{ $json.person_id }}",
"description": "Mensagem: {{ $('Code').item.json.message }}",
"tags": ["whatsapp"]
}
}
Enviar Resposta pelo WhatsApp
# Via Evolution API
curl -X POST "https://evolution.memudecore.com.br/message/sendText/krayin-whatsapp" \
-H "Authorization: Bearer SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"number": "5511987654321",
"text": "Olá! Recebemos sua mensagem. Em breve um consultor entrará em contato."
}'
3. Frontend Operaes (TypeScript)
Configuração do Cliente API
api/krayin.ts
import axios, { AxiosInstance, AxiosError } from 'axios';
interface KrayinConfig {
baseURL: string;
token?: string;
}
interface LoginResponse {
token: string;
message: string;
}
interface Lead {
id: number;
title: string;
source_id: number;
lead_type_id: number;
sales_owner_id: number;
status_id: number;
value: number;
expected_close_date?: string;
description?: string;
contact_person?: Person;
organization?: Organization;
created_at: string;
updated_at: string;
}
interface Person {
id: number;
name: string;
emails: Array<{ value: string; label: string }>;
contact_numbers: Array<{ value: string; label: string }>;
}
interface Organization {
id: number;
name: string;
}
interface PaginatedResponse<T> {
data: T[];
links: {
first: string;
last: string;
prev: string | null;
next: string | null;
};
meta: {
current_page: number;
from: number;
last_page: number;
per_page: number;
to: number;
total: number;
};
}
class KrayinAPI {
private client: AxiosInstance;
private token: string | null = null;
constructor(config: KrayinConfig) {
this.client = axios.create({
baseURL: config.baseURL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
if (config.token) {
this.setToken(config.token);
}
// Interceptor para erros
this.client.interceptors.response.use(
response => response,
(error: AxiosError) => {
if (error.response?.status === 401) {
// Token expirado - redirecionar para login
this.token = null;
window.location.href = '/login';
}
return Promise.reject(error);
}
);
}
setToken(token: string) {
this.token = token;
this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
// ==================== AUTH ====================
async login(email: string, password: string): Promise<string> {
const params = new URLSearchParams();
params.append('email', email);
params.append('password', password);
params.append('device_name', 'Operaes');
const response = await this.client.post<LoginResponse>('/login', params, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
this.setToken(response.data.token);
return response.data.token;
}
// ==================== LEADS ====================
async getLeads(params?: {
page?: number;
per_page?: number;
search?: string;
source_id?: number;
status_id?: number;
sales_owner_id?: number;
}): Promise<PaginatedResponse<Lead>> {
const response = await this.client.get<PaginatedResponse<Lead>>('/leads', { params });
return response.data;
}
async getLead(id: number): Promise<Lead> {
const response = await this.client.get<{ data: Lead }>(`/leads/${id}`);
return response.data.data;
}
async createLead(data: Partial<Lead>): Promise<Lead> {
const response = await this.client.post<{ data: Lead }>('/leads', data);
return response.data.data;
}
async updateLead(id: number, data: Partial<Lead>): Promise<Lead> {
const response = await this.client.put<{ data: Lead }>(`/leads/${id}`, data);
return response.data.data;
}
async deleteLead(id: number): Promise<void> {
await this.client.delete(`/leads/${id}`);
}
async convertLead(id: number): Promise<void> {
await this.client.post(`/leads/${id}/convert`);
}
// ==================== CONTACTS ====================
async getPersons(params?: {
page?: number;
search?: string;
}): Promise<PaginatedResponse<Person>> {
const response = await this.client.get<PaginatedResponse<Person>>('/contacts/persons', { params });
return response.data;
}
async createPerson(data: Partial<Person>): Promise<Person> {
const response = await this.client.post<{ data: Person }>('/contacts/persons', data);
return response.data.data;
}
// ==================== ACTIVITIES ====================
async getActivities(leadId: number): Promise<any[]> {
const response = await this.client.get('/activities', {
params: { lead_id: leadId }
});
return response.data.data;
}
async createActivity(data: {
type: 'call' | 'meeting' | 'lunch' | 'email' | 'note';
title: string;
lead_id: number;
description?: string;
scheduled_from?: string;
scheduled_to?: string;
}): Promise<any> {
const response = await this.client.post('/activities', data);
return response.data.data;
}
}
// Singleton
export const krayinAPI = new KrayinAPI({
baseURL: 'https://crm.memudecore.com.br/api/v1',
});
export default krayinAPI;
Hook React para Leads
hooks/useLeads.ts
import { useState, useEffect, useCallback } from 'react';
import krayinAPI from '../api/krayin';
interface UseLeadsOptions {
page?: number;
perPage?: number;
search?: string;
}
export function useLeads(options: UseLeadsOptions = {}) {
const [leads, setLeads] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const [pagination, setPagination] = useState({
currentPage: 1,
lastPage: 1,
total: 0,
});
const fetchLeads = useCallback(async () => {
try {
setLoading(true);
const response = await krayinAPI.getLeads({
page: options.page || 1,
per_page: options.perPage || 15,
search: options.search,
});
setLeads(response.data);
setPagination({
currentPage: response.meta.current_page,
lastPage: response.meta.last_page,
total: response.meta.total,
});
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}, [options.page, options.perPage, options.search]);
useEffect(() => {
fetchLeads();
}, [fetchLeads]);
const createLead = async (data: any) => {
const lead = await krayinAPI.createLead(data);
setLeads(prev => [lead, ...prev]);
return lead;
};
const updateLead = async (id: number, data: any) => {
const lead = await krayinAPI.updateLead(id, data);
setLeads(prev => prev.map(l => l.id === id ? lead : l));
return lead;
};
const deleteLead = async (id: number) => {
await krayinAPI.deleteLead(id);
setLeads(prev => prev.filter(l => l.id !== id));
};
return {
leads,
loading,
error,
pagination,
refetch: fetchLeads,
createLead,
updateLead,
deleteLead,
};
}
Componente de Lista de Leads
components/LeadsList.tsx
import React, { useState } from 'react';
import { useLeads } from '../hooks/useLeads';
export function LeadsList() {
const [page, setPage] = useState(1);
const [search, setSearch] = useState('');
const { leads, loading, pagination, refetch } = useLeads({
page,
perPage: 10,
search,
});
if (loading) return <div>Carregando...</div>;
return (
<div className="leads-list">
<input
type="text"
placeholder="Buscar leads..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<table>
<thead>
<tr>
<th>Título</th>
<th>Valor</th>
<th>Vendedor</th>
<th>Status</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
{leads.map(lead => (
<tr key={lead.id}>
<td>{lead.title}</td>
<td>R$ {lead.value?.toLocaleString('pt-BR')}</td>
<td>{lead.sales_owner?.name}</td>
<td>{lead.status?.name}</td>
<td>
<button onClick={() => window.location.href = `/leads/${lead.id}`}>
Ver
</button>
</td>
</tr>
))}
</tbody>
</table>
<div className="pagination">
<button
disabled={page === 1}
onClick={() => setPage(p => p - 1)}
>
Anterior
</button>
<span>Página {pagination.currentPage} de {pagination.lastPage}</span>
<button
disabled={page === pagination.lastPage}
onClick={() => setPage(p => p + 1)}
>
Próxima
</button>
</div>
</div>
);
}
4. Outras Integrações
Google Calendar
O Krayin tem integração nativa com Google Calendar:
# .env
GOOGLE_CLIENT_ID=xxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=xxxxx
GOOGLE_REDIRECT_URI=https://crm.memudecore.com.br/google/oauth
GOOGLE_WEBHOOK_URI=https://crm.memudecore.com.br/google/webhook
IMAP Email
Configuração em: Settings → Email Configuration
WebForms
Criar formulários de captura em: Settings → WebForms
📚 Recursos Adicionais
- N8N Docs: https://docs.n8n.io
- Evolution API: https://doc.evolution-api.com
- Krayin API Docs: https://crm.memudecore.com.br/api/admin/documentation
Última atualização: Janeiro 2026