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": "G \u2014 Off-Market Lead Ingestor (Apify \u2192 Portal API)",
"description": "Recebe leads do Apify via webhook, normaliza e chama POST /api/offmarket-leads/manual (auto-pipeline completo: score + price-intel + match-buyers + deal-eval). Nunca escreve directamente no Supabase.",
"active": true,
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "offmarket-new",
"responseMode": "onReceived",
"options": {}
},
"id": "g001",
"name": "Webhook \u2014 Novo Lead Off-Market",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
200,
300
]
},
{
"parameters": {
"jsCode": "const raw = $input.first().json;\nconst body = raw.body || raw;\n// Normalizar para formato esperado por /api/offmarket-leads/manual\nconst lead = {\n nome: body.nome || body.title || body.address || 'Lead Apify ' + new Date().toISOString().substring(0,10),\n cidade: body.cidade || body.city || body.location || 'Lisboa',\n tipo_ativo: body.tipo_ativo || body.propertyType || 'apartamento',\n // Campos opcionais enriquecidos\n localizacao: body.localizacao || body.morada || body.address || null,\n price_ask: body.price_ask || body.price ? Number(body.price_ask || body.price) : null,\n area_m2: body.area_m2 || body.area ? Number(body.area_m2 || body.area) : null,\n contacto: body.contacto || body.contact || null,\n owner_name: body.owner_name || body.owner || null,\n contact_phone_owner: body.contact_phone_owner || body.phone || null,\n source: body.source || 'apify',\n urgency: body.urgency || 'unknown',\n notes: body.notes || body.notas || body.description\n ? (body.notes || body.notas || body.description || '').substring(0, 500)\n : null\n};\n// Limpar nulos\nObject.keys(lead).forEach(k => { if (lead[k] === null) delete lead[k]; });\nreturn [{ json: lead }];"
},
"id": "g002",
"name": "Code \u2014 Normalizar Lead",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
420,
300
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.PORTAL_URL }}/api/offmarket-leads/manual",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "x-cron-secret",
"value": "={{ $env.CRON_SECRET }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {}
},
"id": "g003",
"name": "Portal API \u2014 Criar Lead + Auto-Pipeline",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
640,
300
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "const resp = $input.first().json;\n// Portal API retorna: { success, lead_id, score, nome, cidade, ... }\nif (!resp.success) {\n console.log('[WF-G] Portal API error:', JSON.stringify(resp).substring(0, 300));\n return [{ json: { id: null, _error: true, _raw: JSON.stringify(resp).substring(0, 200) } }];\n}\n// score j\u00e1 calculado pelo pipeline completo\nconst score = resp.score ?? 0;\nconst tier = score >= 70 ? 'A' : score >= 40 ? 'B' : 'C';\nconsole.log('[WF-G] Lead criado:', resp.lead_id, 'score:', score, 'tier:', tier);\nreturn [{ json: { ...resp, id: resp.lead_id, computed_score: score, tier } }];"
},
"id": "g004",
"name": "Code \u2014 Extrair Score do Pipeline",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
860,
300
]
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{ $json.computed_score }}",
"operation": "largerEqual",
"value2": 70
}
]
}
},
"id": "g005",
"name": "IF \u2014 Score \u226570?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
1080,
300
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.resend.com/emails",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $env.RESEND_API_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ from: 'Deal Machine <noreply@agencygroup.pt>', to: ['geral@agencygroup.pt'], subject: '\ud83d\udd25 Off-Market Score Alto: ' + $json.nome + ' (' + $json.computed_score + '/100 Tier ' + $json.tier + ')', text: 'LEAD OFF-MARKET\\n\\nNome: ' + $json.nome + '\\nScore: ' + $json.computed_score + '/100 Tier ' + $json.tier + '\\nCidade: ' + ($json.cidade||'\u2014') + '\\nPre\u00e7o: ' + ($json.price_ask ? '\u20ac' + Number($json.price_ask/1000).toFixed(0) + 'K' : '\u2014') + '\\nFonte: ' + ($json.source||'apify') + '\\nPortal: ' + $env.PORTAL_URL + '/portal' }) }}",
"options": {}
},
"id": "g006",
"name": "Email \u2014 Alerta Score Alto",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1300,
200
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "const lead = $input.first().json;\nreturn [{ json: { status: 'ok', id: lead.id || lead.lead_id, score: lead.computed_score, tier: lead.tier, nome: lead.nome } }];"
},
"id": "g007",
"name": "Code \u2014 Done",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1300,
400
]
}
],
"connections": {
"Webhook \u2014 Novo Lead Off-Market": {
"main": [
[
{
"node": "Code \u2014 Normalizar Lead",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Normalizar Lead": {
"main": [
[
{
"node": "Portal API \u2014 Criar Lead + Auto-Pipeline",
"type": "main",
"index": 0
}
]
]
},
"Portal API \u2014 Criar Lead + Auto-Pipeline": {
"main": [
[
{
"node": "Code \u2014 Extrair Score do Pipeline",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Extrair Score do Pipeline": {
"main": [
[
{
"node": "IF \u2014 Score \u226570?",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 Score \u226570?": {
"main": [
[
{
"node": "Email \u2014 Alerta Score Alto",
"type": "main",
"index": 0
}
],
[
{
"node": "Code \u2014 Done",
"type": "main",
"index": 0
}
]
]
},
"Email \u2014 Alerta Score Alto": {
"main": [
[
{
"node": "Code \u2014 Done",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner"
},
"tags": [
{
"name": "offmarket"
},
{
"name": "core"
}
]
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
G — Off-Market Lead Ingestor (Apify → Portal API). Uses httpRequest. Webhook trigger; 7 nodes.
Source: https://github.com/cfeiteira73-cmd/agency-group-platform/blob/4d1d3507093100c61ec89737a20e8a750ef06f9f/n8n-workflows/wf_g_current.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.
This workflow enables the submission of business-critical URLs via the Google Indexing API and IndexNow.
Never miss important website updates again! This workflow automatically tracks changes on dynamic websites (think React apps, JavaScript-heavy sites) and sends you instant email notifications when som
This template implements a recursive web crawler inside n8n. Starting from a given URL, it crawls linked pages up to a maximum depth (default: 3), extracts text and links, and returns the collected co
Crawl Space & Foundation Repair Intake AI - Vapi MVP (Client Template). Uses httpRequest, googleSheets. Webhook trigger; 14 nodes.