Overview
Energy CMMS integrates with n8n for powerful workflow automation, including document processing, AI-powered chat, and webhook-based notifications.Architecture
Configuration
Environment Detection
The system automatically configures n8n URLs based on environment:Webhook Implementations
1. Document Creation Notification
Notifies n8n when a new document is created:documentos/utils_n8n.py
2. Document Processing (PDF + Metadata)
Extract text and metadata from documents using AI:@shared_task(name='documentos.tasks.extract_document_metadata')
def extract_document_metadata(revision_id):
"""
Tarea para extraer texto y metadatos de un documento (PDF, etc.)
"""
from .models import Revision
from django.conf import settings
import requests
try:
revision = Revision.objects.get(pk=revision_id)
revision.estado_extraccion = 'PROCESANDO'
revision.save()
# Prepare callback URL for n8n to send results back
base_callback_url = settings.INTERNAL_SITE_URL
internal_callback_url = f"{base_callback_url}/documentos/api/callback-procesamiento/{revision.id}/"
payload = {
'revision_id': revision.id,
'documento_id': revision.documento.id,
'filename': os.path.basename(revision.archivo.name),
'file_url': revision.archivo.url, # MinIO signed URL
'file_key': revision.archivo.name,
'tipo_documento': revision.documento.tipo_documento.nombre,
'callback_url': internal_callback_url,
'metadatos_requeridos': list(
revision.documento.tipo_documento.metadatos_config.values_list('nombre', flat=True)
)
}
n8n_url = settings.N8N_PROCESS_DOCUMENT_WEBHOOK_URL
response = requests.post(n8n_url, json=payload, timeout=10)
logger.info(f"Revision {revision_id}: Enviada a n8n. Status: {response.status_code}")
return True
except Exception as e:
logger.error(f"Error en extract_document_metadata: {str(e)}")
return False
{
"name": "Process Document - PDF + Metadata",
"nodes": [
{
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"parameters": {
"path": "process-document",
"responseMode": "onReceived"
}
},
{
"name": "Download PDF",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$json.file_url}}",
"method": "GET",
"responseFormat": "file"
}
},
{
"name": "Extract Text (PyMuPDF)",
"type": "n8n-nodes-base.code",
"parameters": {
"language": "python",
"code": "import fitz\n\npdf_data = items[0]['binary']['data']\ndoc = fitz.open(stream=pdf_data)\n\ntext = ''\nfor page in doc:\n text += page.get_text()\n\nreturn {'texto': text, 'num_pages': len(doc)}"
}
},
{
"name": "Extract Metadata (Gemini)",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent",
"method": "POST",
"authentication": "genericCredentialType",
"headers": {
"Content-Type": "application/json"
},
"body": {
"contents": [
{
"parts": [
{
"text": "Extract these fields from the document: {{$json.metadatos_requeridos}}. Document text: {{$json.texto}}"
}
]
}
]
}
}
},
{
"name": "Callback to Django",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "={{$json.callback_url}}",
"method": "POST",
"body": {
"texto": "={{$json.texto}}",
"metadata": "={{$json.gemini_response}}",
"num_paginas": "={{$json.num_pages}}"
}
}
}
]
}
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import json
@csrf_exempt
def callback_procesamiento(request, revision_id):
"""
Callback endpoint para recibir resultados de n8n
"""
if request.method == 'POST':
try:
data = json.loads(request.body)
revision = Revision.objects.get(pk=revision_id)
# Update extracted text
if 'texto' in data:
revision.documento.contenido_texto = data['texto']
revision.documento.save()
# Update metadata
if 'metadata' in data:
revision.datos_extraidos = data['metadata']
revision.estado_extraccion = 'COMPLETADO'
revision.save()
return JsonResponse({'status': 'success'})
except Exception as e:
logger.error(f"Error en callback: {e}")
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Method not allowed'}, status=405)
3. AI Chat Integration
Implement document chat with Gemini:documentos/views.py
Callback URL Configuration
Internal vs Public URLs
Troubleshooting
Connection refused to n8n
Connection refused to n8n
Symptoms:
ConnectionError: Connection refused- Webhook calls timeout
- Verify n8n is running:
- Check network connectivity:
- Verify environment variables:
Callback URL incorrect port
Callback URL incorrect port
Error: n8n tries to callback to wrong port (e.g.,
localhost:5000 instead of localhost:8000)Solution from N8N_DJANGO_SETUP.md:- Open n8n workflow editor
- Find “Callback to Django” node
- Change URL from hardcoded value to:
{{$json["callback_url"]}} - Ensure Django sends correct
callback_urlin webhook payload
Webhook not triggering
Webhook not triggering
Check webhook registration in n8n:
- Go to n8n UI:
http://n8n:5678 - Open workflow
- Verify webhook path matches settings
- Test webhook:
Best Practices
Use Async Tasks
Always trigger n8n webhooks from Celery tasks, never synchronously
Implement Timeouts
Set reasonable timeouts (5-30s) to prevent hanging requests
Error Logging
Log all webhook calls and responses for debugging
Callback Security
Use internal URLs for callbacks in production environments
Related Resources
Celery Tasks
Configure background task processing
MinIO Storage
File storage for document processing