Guía de integración con n8n
Respuesta rápida: Usa el nodo de trigger Webhook de n8n para recibir los eventos report.completed de Surmado. Apunta webhook_url de Surmado a la URL del webhook de n8n, filtra por event y report.product, y envía el payload a Google Sheets, Slack, Postgres o cualquier otra integración. n8n autoalojado es gratis. n8n Cloud cuesta $20/mes.
Tiempo de lectura: 16 minutos
Requisitos previos
- Una instancia de n8n (autoalojada con Docker, o n8n Cloud)
- Clave de API de Surmado (
sur_live_...): créala en app.surmado.com → Settings → API Keys - Una URL pública para tu webhook de n8n (n8n Cloud se encarga de esto; si lo autoalojas, necesitas un dominio o un túnel)
Por qué n8n
- Se puede autoalojar: tu flujo de trabajo corre en tu propia infraestructura (Docker, Kubernetes, servidor propio)
- Código abierto: puedes inspeccionar cada nodo y ampliarlo con código propio
- Sin cobro por tarea: ejecuciones ilimitadas si lo autoalojas (solo pagas el servidor)
- Más de 400 nodos incluidos: Notion, Google Sheets, Slack, Airtable, PostgreSQL, HTTP Request, Code, etc.
- Bóveda de credenciales: guarda tu clave de API de Surmado una sola vez y úsala en todos tus flujos de trabajo
Inicio rápido: autoalojado con Docker
docker volume create n8n_data
docker run -d --name n8n \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
-e N8N_HOST="n8n.yourdomain.com" \
-e WEBHOOK_URL="https://n8n.yourdomain.com/" \
n8nio/n8n
Luego configura un proxy inverso de n8n.yourdomain.com hacia http://localhost:5678 con Caddy, Nginx o Traefik, para que Surmado pueda llegar a tu endpoint de webhook por HTTPS.
Guarda tu clave de API de Surmado
En n8n, ve a Credentials → New → Header Auth (o usa la credencial integrada del nodo genérico HTTP Request):
- Name:
Surmado API - Header name:
Authorization - Header value:
Bearer sur_live_YOUR_API_KEY
Usa esta credencial en cualquier nodo HTTP Request que llame a api.surmado.com.
El patrón de integración
- Un nodo de trigger Webhook. n8n te da una URL como
https://n8n.yourdomain.com/webhook/abc123. - Configura Surmado para enviar por POST los informes completados a esa URL con
webhook_urlal crear un informe. - Un nodo IF para filtrar por
eventyreport.product. - (Opcional) Un nodo HTTP Request para llamar a
GET /v1/reports/{{ $json.report.id }}y obtener datos más profundos. - Nodos de destino: Google Sheets, Slack, Notion, Postgres, etc.
Ejemplo de payload del nodo Webhook
{
"event": "report.completed",
"timestamp": "2026-04-15T12:00:00+00:00",
"report": {
"id": "rpt_abc123xyz",
"token": "SIG-2026-04-A1B2C",
"product": "signal",
"status": "completed",
"tier": "basic",
"data_url": "https://api.surmado.com/v1/reports/rpt_abc123xyz",
"pdf_url": "https://api.surmado.com/v1/reports/view/VIEW_TOKEN",
"summary": {
"business_name": "Acme Coffee Roasters",
"presence_score": 34.2,
"authority_score": 42.7,
"competitive_rank": 4,
"top_competitor": "Blue Bottle"
}
}
}
Informes de Strategy (
"product": "solutions") llegan sin objetosummary: solo traenreport.tokenyreport.pdf_url.
Flujo 1: AI Visibility a Google Sheets
Nodo 1: trigger Webhook
- HTTP method: POST
- Path:
surmado-signal(n8n generaráhttps://n8n.yourdomain.com/webhook/surmado-signal) - Response mode: Immediately (devuelve 200 OK en cuanto n8n recibe la solicitud)
Pasa esa URL a Surmado:
curl -X POST https://api.surmado.com/v1/reports/signal \
-H "Authorization: Bearer sur_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brand_slug": "acme_corp",
"url": "https://acme.com",
"email": "founder@acme.com",
"industry": "B2B SaaS",
"location": "United States",
"persona": "Small business owners",
"pain_points": "Losing track of customer conversations",
"brand_details": "Simple CRM with email integration",
"direct_competitors": "HubSpot, Pipedrive",
"webhook_url": "https://n8n.yourdomain.com/webhook/surmado-signal"
}'
Haz clic en Listen for test event en el nodo Webhook y luego genera un informe para que n8n capture la forma del payload.
Nodo 2: IF
Condición 1: {{ $json.event }} equals report.completed
Condición 2: {{ $json.report.product }} equals signal
Combínalas con: AND
Nodo 3: Google Sheets, Append row
Operation: Append Spreadsheet / Sheet: tu hoja de seguimiento
Asignación de columnas:
- Date:
{{ $json.timestamp }} - Token:
{{ $json.report.token }} - Business:
{{ $json.report.summary.business_name }} - Presence Score:
{{ $json.report.summary.presence_score }} - Authority Score:
{{ $json.report.summary.authority_score }} - Competitive Rank:
{{ $json.report.summary.competitive_rank }} - Top Competitor:
{{ $json.report.summary.top_competitor }}
Activa el flujo de trabajo. Cada informe de AI Visibility completado queda registrado.
Flujo 2: Site Audit con alerta condicional en Slack
Webhook → IF (report.product = scan) → Google Sheets (siempre) → IF (report.summary.performance_score < 70) → Slack.
Mensaje de Slack:
:warning: Performance regression on {{ $json.report.summary.business_name }}
Performance: {{ $json.report.summary.performance_score }} (below 70 threshold)
SEO: {{ $json.report.summary.seo_score }}
Accessibility: {{ $json.report.summary.accessibility_score }}
Critical issues: {{ $json.report.summary.critical_issues_count }}
View report: {{ $json.report.pdf_url }}
Flujo 3: resumen en PDF de Strategy
Strategy no incluye resumen. El patrón es este:
- Webhook → IF (
report.product = solutions) → Send Email (Gmail, SMTP, SendGrid).
Cuerpo del correo:
<p>Strategy report <code>{{ $json.report.token }}</code> is ready.</p>
<p><a href="{{ $json.report.pdf_url }}">View the full report (PDF)</a></p>
Cómo obtener datos más profundos con la API REST
El resumen del webhook cubre la mayoría de las necesidades. Para la variación por plataforma, los problemas por página o el detalle de los vacíos de contenido, agrega un nodo HTTP Request después del filtro:
Nodo: HTTP Request
- Method: GET
- URL:
https://api.surmado.com/v1/reports/{{ $json.report.id }} - Authentication: credencial predefinida → Surmado API (el Header Auth que creaste arriba)
- Response format: JSON
Los nodos siguientes pueden usar {{ $json.public_intelligence.data.platform_variance.chatgpt.presence_score }}, {{ $json.public_intelligence.data.audit_data.enrichment.content_gaps.executive_summary }}, etc.
Verificación de la firma del webhook
En producción, verifica el encabezado X-Surmado-Signature antes de confiar en el payload. Surmado firma los webhooks con HMAC-SHA256 sobre una representación JSON canonicalizada.
Nodo Function: verificar la firma
const crypto = require('crypto');
const secret = $env.SURMADO_WEBHOOK_SECRET;
const signature = $('Webhook').first().json.headers['x-surmado-signature'];
const body = $('Webhook').first().json.body;
function canonicalize(value) {
if (value === null || typeof value !== 'object') return JSON.stringify(value);
if (Array.isArray(value)) return '[' + value.map(canonicalize).join(',') + ']';
const keys = Object.keys(value).sort();
return '{' + keys.map(k => JSON.stringify(k) + ':' + canonicalize(value[k])).join(',') + '}';
}
function asciiEscape(str) {
return str.replace(/[\u0080-\uffff]/g, c =>
'\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
);
}
const canonical = asciiEscape(canonicalize(body));
const expected = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'))) {
throw new Error('Invalid webhook signature');
}
return [{ json: body }];
Cadena: Webhook → Function (verificar) → IF (filtro) → nodos de destino.
Formatos de token
- AI Visibility:
SIG-YYYY-MM-XXXXX(regex:SIG-\d{4}-\d{2}-[A-Z0-9]{5}) - Site Audit:
SCN-YYYY-MM-XXXXX(regex:SCN-\d{4}-\d{2}-[A-Z0-9]{5}) - Strategy:
SOL-YYYY-MM-XXXXX(regex:SOL-\d{4}-\d{2}-[A-Z0-9]{5})
Los tokens no expiran. Las URL de descarga firmadas de la API REST son válidas por unos 15 minutos. Los enlaces mágicos de pdf_url en los webhooks son válidos por unos 30 días.
Patrones avanzados
Enruta por producto con Switch
Usa un nodo Switch sobre $json.report.product con casos para signal, scan y solutions. Cada caso lleva a un flujo específico de cada producto. Una sola URL de webhook atiende todos los tipos de informe.
Guarda en Postgres para analizar series de tiempo
Agrega un nodo Postgres después del filtro IF que haga INSERT de (timestamp, token, report_id, product, business_name, presence_score, authority_score, ...) en una tabla de informes. Luego consúltala con Grafana o Metabase para ver tendencias.
Enrutamiento multicliente para agencias
Usa un Data store (o una base de datos externa) que relacione business_name / brand_slug con la configuración de cada cliente (qué canal de Slack, qué pestaña de la hoja, etc.). Consulta esa información en cada informe y enruta de forma dinámica.
Solución de problemas
El webhook no se dispara: Activa el flujo de trabajo (n8n solo procesa webhooks cuando está activo, no durante las pruebas). Confirma que webhook_url se definió al crear el informe. Usa webhook.site como URL de prueba para verificar que Surmado envía el payload esperado.
El nodo HTTP Request devuelve 401: La credencial no está asociada, o a la clave le falta el prefijo Bearer . El valor completo del encabezado debe ser Bearer sur_live_....
Los campos llegan vacíos a los nodos siguientes: Confirma el tipo de producto. Los webhooks de Strategy no incluyen report.summary; solo vienen report.token y report.pdf_url.
No se puede llegar al webhook autoalojado: Surmado exige HTTPS. Si corres n8n en localhost, usa un túnel (ngrok, Cloudflare Tunnel) o despliégalo detrás de un proxy inverso con un certificado TLS válido.
La verificación de firma falla con caracteres no ASCII: La función canonicalize de arriba ya lo resuelve. Si escribiste la tuya, asegúrate de escapar a ASCII los caracteres no ASCII para que coincida con la salida de json.dumps(..., ensure_ascii=True) de Python que usa Surmado.
Relacionado: Guía de integración de la API | Datos estructurados e Intelligence Tokens | Integración con Zapier | Integración con Make | Integración con Notion