This workflow follows the Error Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "CRM Outreach Engine v2 (API-Based)",
"nodes": [
{
"parameters": {
"content": "## CRM Outreach Engine v2\n\n**Substitui o workflow MySQL legado por chamadas \u00e0 API do CRM.**\n\n### M\u00f3dulos:\n- **M\u00f3dulo 1 - Importa\u00e7\u00e3o**: Schedule 8h + Manual \u2192 Apify \u2192 POST /customers/import\n- **M\u00f3dulo 2 - Segmenta\u00e7\u00e3o**: Schedule a cada 2h \u2192 POST /outreach/segment\n- **M\u00f3dulo 3 - Envio**: Schedule 9h, 14h, 18h \u2192 POST /outreach/send\n- **Error Handler**: Captura erros globais\n\n### Autentica\u00e7\u00e3o (API Key est\u00e1tica \u2014 N\u00c3O usa JWT):\nCrie uma credencial **Header Auth** no n8n com nome exato `CRM API Key`:\n- **Header Name**: `X-Api-Key`\n- **Header Value**: (ver secret DIAX_SERVICE_API_KEY no GitHub / AWS SM)\n\n> \u26a0\ufe0f N\u00c3O use `Authorization: Bearer <jwt>`. JWTs expiram em 60min e quebram o agendamento.\n> A API aceita `X-Api-Key` com uma chave est\u00e1tica para automa\u00e7\u00f5es M2M.\n\n**API Base:** `https://api.alexandrequeiroz.com.br/api/v1`",
"height": 320,
"width": 460,
"color": 5
},
"id": "sticky-readme",
"name": "Leia-me",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-480,
-120
]
},
{
"parameters": {
"content": "### M\u00f3dulo 1: Importa\u00e7\u00e3o\nTrigger: Di\u00e1rio \u00e0s 8h + Manual\n1. Busca config (URL do Apify dataset)\n2. Baixa dados do Apify\n3. Normaliza para BulkImportRequest\n4. POST /customers/import",
"height": 180,
"width": 280,
"color": 6
},
"id": "sticky-mod1",
"name": "Nota - M\u00f3dulo 1",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-480,
240
]
},
{
"parameters": {
"content": "### M\u00f3dulo 2: Segmenta\u00e7\u00e3o\nTrigger: A cada 2 horas\nSegmenta leads HOT/WARM/COLD via\nPOST /outreach/segment",
"height": 140,
"width": 280,
"color": 4
},
"id": "sticky-mod2",
"name": "Nota - M\u00f3dulo 2",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-480,
460
]
},
{
"parameters": {
"content": "### M\u00f3dulo 3: Envio de Campanha\nTrigger: 9h, 14h e 18h\nCria e enfileira campanha via\nPOST /outreach/send",
"height": 140,
"width": 280,
"color": 3
},
"id": "sticky-mod3",
"name": "Nota - M\u00f3dulo 3",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-480,
640
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * *"
}
]
}
},
"id": "trigger-import-schedule",
"name": "Schedule - Importa\u00e7\u00e3o (8h)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
60,
280
]
},
{
"parameters": {},
"id": "trigger-import-manual",
"name": "Manual - Importa\u00e7\u00e3o",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
60,
420
]
},
{
"parameters": {
"method": "GET",
"url": "https://api.alexandrequeiroz.com.br/api/v1/outreach/config",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {}
},
"id": "http-get-config",
"name": "GET /outreach/config",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
320,
350
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "GET",
"url": "={{ $json.apifyDatasetUrl }}",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "http-get-apify",
"name": "GET Apify Dataset",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
580,
350
]
},
{
"parameters": {
"jsCode": "// Normaliza os itens do Apify para o formato BulkImportRequest do CRM\nconst apifyItems = $input.all();\n\nfunction extractWhatsApp(phone) {\n if (!phone) return null;\n // Tenta extrair de URLs wa.me/5511...\n const waMatch = phone.match(/wa\\.me\\/(\\d+)/);\n if (waMatch) return '+' + waMatch[1];\n // Limpa o n\u00famero e assume que \u00e9 WhatsApp se tiver DDD v\u00e1lido\n const cleaned = phone.replace(/[^0-9+]/g, '');\n if (cleaned.length >= 10) return cleaned.startsWith('+') ? cleaned : '+55' + cleaned.replace(/^0/, '');\n return null;\n}\n\nfunction cleanPhone(phone) {\n if (!phone) return null;\n const cleaned = phone.replace(/[^0-9+]/g, '');\n if (cleaned.length < 8) return null;\n return cleaned.startsWith('+') ? cleaned : '+55' + cleaned.replace(/^0/, '');\n}\n\nconst customers = apifyItems.map(item => {\n const d = item.json;\n const email = Array.isArray(d.emails) && d.emails.length > 0 ? d.emails[0] : null;\n const phone = cleanPhone(d.phone);\n const whatsApp = extractWhatsApp(d.phone) || phone;\n\n // Monta tags: cidade + categoria\n const tagParts = [];\n if (d.city) tagParts.push(d.city.toLowerCase().replace(/\\s+/g, '-'));\n if (d.categoryName) tagParts.push(d.categoryName.toLowerCase().replace(/\\s+/g, '-'));\n if (d.website) tagParts.push('tem-site');\n else tagParts.push('sem-site');\n tagParts.push('google-maps');\n\n // Monta notes com contexto do lead\n const notesParts = [];\n if (d.address) notesParts.push(`Endere\u00e7o: ${d.address}`);\n if (d.totalScore) notesParts.push(`Avalia\u00e7\u00e3o Google: ${d.totalScore}`);\n if (d.reviewsCount) notesParts.push(`Reviews: ${d.reviewsCount}`);\n if (d.website) notesParts.push(`Site: ${d.website}`);\n\n return {\n name: d.title || d.name || 'Sem nome',\n email: email,\n phone: phone,\n whatsApp: whatsApp,\n companyName: d.title || d.name || null,\n website: d.website || null,\n tags: tagParts.join(','),\n notes: notesParts.join(' | ') || null\n };\n}).filter(c => c.name && c.name !== 'Sem nome' && (c.phone || c.email));\n\nif (customers.length === 0) {\n return [{ json: { skipped: true, reason: 'Nenhum lead v\u00e1lido encontrado no dataset Apify' } }];\n}\n\nreturn [{\n json: {\n customers: customers,\n source: 11\n }\n}];"
},
"id": "code-normalize-apify",
"name": "Normalizar Dados Apify",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
840,
350
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.alexandrequeiroz.com.br/api/v1/customers/import",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"contentType": "json",
"body": "={{ JSON.stringify($json) }}",
"options": {}
},
"id": "http-post-import",
"name": "POST /customers/import",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1100,
350
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 2
}
]
}
},
"id": "trigger-segmentation",
"name": "Schedule - Segmenta\u00e7\u00e3o (2h)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
60,
560
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.alexandrequeiroz.com.br/api/v1/outreach/segment",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {}
},
"id": "http-post-segment",
"name": "POST /outreach/segment",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
320,
560
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const result = $input.first().json;\nconsole.log(`[Segmenta\u00e7\u00e3o] Total: ${result.totalProcessed} | HOT: ${result.hotCount} | WARM: ${result.warmCount} | COLD: ${result.coldCount}`);\nreturn [{ json: { success: true, result } }];"
},
"id": "code-log-segmentation",
"name": "Log Segmenta\u00e7\u00e3o",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
580,
560
]
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9,14,18 * * *"
}
]
}
},
"id": "trigger-send",
"name": "Schedule - Envio (9h, 14h, 18h)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
60,
740
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.alexandrequeiroz.com.br/api/v1/outreach/send",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {}
},
"id": "http-post-send",
"name": "POST /outreach/send",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
320,
740
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const result = $input.first().json;\nconst campaignId = result.campaignId || 'N/A';\nconst queued = result.queuedCount ?? 0;\nconst skipped = result.skippedCount ?? 0;\nconsole.log(`[Envio de Campanha] CampaignId: ${campaignId} | Enfileirados: ${queued} | Pulados: ${skipped}`);\nif (result.skippedReasons && result.skippedReasons.length > 0) {\n console.log(`[Envio de Campanha] Motivos pulados: ${result.skippedReasons.join('; ')}`);\n}\nreturn [{ json: { success: true, campaignId, queued, skipped, result } }];"
},
"id": "code-log-send",
"name": "Log Envio",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
580,
740
]
},
{
"parameters": {},
"id": "trigger-error",
"name": "Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"typeVersion": 1,
"position": [
60,
940
]
},
{
"parameters": {
"jsCode": "const error = $input.first().json;\nconst workflow = error.workflow?.name || 'Desconhecido';\nconst node = error.execution?.lastNodeExecuted || 'Desconhecido';\nconst errorMsg = error.execution?.error?.message || JSON.stringify(error);\nconst execId = error.execution?.id || 'N/A';\n\nconsole.error(`[ERRO] Workflow: ${workflow}`);\nconsole.error(`[ERRO] N\u00f3: ${node}`);\nconsole.error(`[ERRO] Execu\u00e7\u00e3o ID: ${execId}`);\nconsole.error(`[ERRO] Mensagem: ${errorMsg}`);\n\n// Retorna resumo do erro para poss\u00edvel integra\u00e7\u00e3o futura (ex: Slack, email)\nreturn [{\n json: {\n alertType: 'workflow_error',\n workflow,\n node,\n execId,\n errorMsg,\n timestamp: new Date().toISOString()\n }\n}];"
},
"id": "code-error-handler",
"name": "Tratar Erro",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
320,
940
]
}
],
"connections": {
"Schedule - Importa\u00e7\u00e3o (8h)": {
"main": [
[
{
"node": "GET /outreach/config",
"type": "main",
"index": 0
}
]
]
},
"Manual - Importa\u00e7\u00e3o": {
"main": [
[
{
"node": "GET /outreach/config",
"type": "main",
"index": 0
}
]
]
},
"GET /outreach/config": {
"main": [
[
{
"node": "GET Apify Dataset",
"type": "main",
"index": 0
}
]
]
},
"GET Apify Dataset": {
"main": [
[
{
"node": "Normalizar Dados Apify",
"type": "main",
"index": 0
}
]
]
},
"Normalizar Dados Apify": {
"main": [
[
{
"node": "POST /customers/import",
"type": "main",
"index": 0
}
]
]
},
"Schedule - Segmenta\u00e7\u00e3o (2h)": {
"main": [
[
{
"node": "POST /outreach/segment",
"type": "main",
"index": 0
}
]
]
},
"POST /outreach/segment": {
"main": [
[
{
"node": "Log Segmenta\u00e7\u00e3o",
"type": "main",
"index": 0
}
]
]
},
"Schedule - Envio (9h, 14h, 18h)": {
"main": [
[
{
"node": "POST /outreach/send",
"type": "main",
"index": 0
}
]
]
},
"POST /outreach/send": {
"main": [
[
{
"node": "Log Envio",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger": {
"main": [
[
{
"node": "Tratar Erro",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "last",
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": ""
},
"meta": {
"templateCredsSetupCompleted": false,
"notes": "ACAO NECESSARIA: Configurar credencial 'CRM API Key' no n8n com Header=X-Api-Key e Value=<DIAX_SERVICE_API_KEY>"
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
CRM Outreach Engine v2 (API-Based). Uses httpRequest, errorTrigger. Scheduled trigger; 18 nodes.
Source: https://github.com/xandeq/diax-crm/blob/af683dd93a91539402592fd9fb178ccfbd439c7c/n8n-workflows/email-marketing-v2.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Workflow A — WhatsApp Lead Intake & Qualification. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 67 nodes.
This automation creates a seamless daily pipeline that: Pulls yesterday's website visitors from Leadfeeder Enriches company data using Apollo.io's powerful database Delivers enriched leads to your Goo
Build authentic Reddit presence and generate qualified leads through AI-powered community engagement that provides genuine value without spam or promotion.
Ghost Rider CRM Import (Lead Processor). Uses httpRequest. Scheduled trigger; 40 nodes.
This workflow runs on scheduled weekly and monthly triggers to generate unified marketing performance reports. It processes multiple websites by collecting analytics data, paid ads performance, and CR