API e integraciones
LinkedFlow habla con el resto de tus herramientas por tres caminos: una API REST con token, un webhook que dispara workflows desde fuera y nodos que llaman a lo tuyo desde dentro de un recorrido. Esta página es el contrato exacto, con ejemplos listos para n8n, Make o un curl.
La API que usa la propia app es la API pública: mismos endpoints, misma autenticación. Para una integración no uses el token del navegador: crea un token de API en Ajustes → Tokens de API, con la caducidad que tú elijas y atado a este workspace.
Los tres caminos
| Camino | Quién llama a quién | Para qué sirve |
|---|---|---|
| API REST | Tu herramienta → LinkedFlow | Crear prospectos y listas, lanzar workflows, leer ejecuciones, resolver aprobaciones, consultar el funnel. |
| Webhook entrante | Tu herramienta → un workflow concreto | Arrancar un recorrido para un prospecto que ya existe en el CRM, con el token del workflow como credencial. |
| Nodos de salida | LinkedFlow → tu herramienta | Avisar a tu CRM o a n8n a mitad del recorrido (Webhook saliente) o pedirle un dato y seguir con la respuesta (Petición HTTP). |
Autenticación
Toda la API vive bajo https://app.linkedflow.pro/api/v1 y se autentica con un token en la cabecera Authorization. Hay dos maneras de conseguirlo, y para integraciones solo sirve la primera.
Token de API (el que quieres para n8n)
En la app, Ajustes → Tokens de API → Nuevo token. Le pones un nombre («n8n · señales diarias»), eliges caducidad —30 días, 90 días, 1 año o sin caducidad— y lo copias: solo se enseña una vez.
| Propiedad | Qué significa |
|---|---|
| Atado a un workspace | El del que se creó, y no se puede mover: aunque tú cambies de workspace en la app o alguien mande la cabecera X-Workspace, ese token sigue escribiendo donde se configuró. |
| Hereda tu rol | No amplía permisos. Si bajas de MANAGER a VIEWER, sus llamadas de escritura empiezan a dar 403. |
| Sobrevive a tu contraseña | Cambiar la contraseña cierra las demás sesiones del navegador, pero no revoca los tokens de API: rotar la contraseña no tumba tus integraciones. |
| Se revoca al instante | Desde la misma pantalla. Deja de funcionar en la siguiente llamada, y ahí ves también cuándo se usó por última vez. |
Consejo: crea un usuario aparte para la máquina («automatizaciones@tuempresa.com») con el rol mínimo que necesite y saca el token desde su cuenta. Así se distingue en la actividad lo que hizo un humano de lo que hizo n8n, y puedes cortarle el acceso sin tocar el tuyo.
Token de sesión (para un script de usar y tirar)
El mismo login de la app devuelve un token, pero caduca a los 30 días y sigue al workspace activo de tu usuario. Vale para probar en un curl; no para una automatización que tiene que aguantar meses:
curl -X POST https://app.linkedflow.pro/api/v1/auth/login \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"email":"tu@email.com","password":"tu-contraseña"}'
{
"user": { "id": "…", "name": "Valen", "current_workspace": { "id": "…", "role": "OWNER" } },
"token": "198|eMdBPK8g…"
}
A partir de ahí, cada llamada lleva dos cabeceras:
Authorization: Bearer 198|eMdBPK8g…
Accept: application/json
El token es tu contraseña. Guárdalo como credencial en n8n (tipo «Header Auth»: nombre Authorization, valor Bearer <token>), nunca en la URL ni en el cuerpo de un nodo. Si un token de API se te escapa, revócalo desde Ajustes → Tokens de API: es inmediato y no afecta a los demás.
Workspace y roles
No hay que mandar el workspace en ninguna llamada: el token trabaja siempre sobre el workspace actual del usuario. Si perteneces a varios, POST /auth/switch-workspace/{workspace} lo cambia. Cada endpoint exige el mismo rol que la pantalla equivalente de la app (VIEWER < MEMBER < MANAGER < ADMIN < OWNER); si te falta rango, la respuesta es un 403. Los roles están explicados en Ajustes.
Consejo para integraciones: crea un usuario aparte para la máquina («automatizaciones@tuempresa.com») con el rol mínimo que necesite. Así puedes cortarle el acceso sin tocar el tuyo, y en la actividad del workspace se distingue lo que hizo un humano de lo que hizo n8n.
Convenciones
- Sin
Accept: application/jsonno hay JSON: la app respondería con una redirección o HTML. Es el error número uno al empezar. - Errores:
401sin token o caducado,403rol insuficiente o suscripción no activa,404fuera de tu workspace,422validación ({"message":"…","errors":{"campo":["…"]}}),429demasiadas llamadas. - Listados: los prospectos vienen paginados (
data,current_page,last_page,total); otros recursos devuelven la colección directa o dentro deitems. Mira siempre la primera respuesta antes de mapear campos. - Idioma: los mensajes de error salen en el idioma de tu usuario. Se puede forzar con
Accept-Language: en. - Ritmo: no hay un límite global, pero los endpoints caros llevan el suyo (el webhook de workflow, 30 llamadas por minuto; el compositor de IA, 20).
Endpoints principales
La lista completa es la de la app; esto es lo que de verdad se usa desde fuera.
Prospectos y listas
| Endpoint | Rol | Qué hace |
|---|---|---|
GET /prospects | VIEWER | Listado paginado. Filtros por search, status, list_id, tag. |
POST /prospects | MEMBER | Crea un prospecto. Acepta list_id para meterlo en una lista de paso. |
GET /prospects/{id} | VIEWER | Ficha completa. |
PUT /prospects/{id} | MEMBER | Edita campos (no el estado: ese lo mueve el motor). |
POST /prospects/bulk-tags | MEMBER | Etiqueta en bloque. |
GET /prospects/{id}/conversation | VIEWER | El hilo real de LinkedIn. |
POST /prospects/{id}/conversation/messages | MANAGER | Envía un DM de verdad (respeta cupos y horario). |
GET · POST /lists | VIEWER · MEMBER | Listar y crear listas. |
POST /lists/{id}/prospects | MEMBER | Añade prospectos existentes a una lista. |
POST /suppression | MANAGER | Manda a alguien a la lista de supresión. |
Workflows y ejecuciones
| Endpoint | Rol | Qué hace |
|---|---|---|
GET /workflows | VIEWER | Listado con estado, versión actual y última ejecución. |
POST /workflows/{id}/run | MANAGER | Lanza sobre list_id o prospect_ids[]. Admite account_id y allow_repeat. |
POST /workflows/{id}/activate · /pause | MANAGER | Enciende o apaga los disparadores. |
POST /workflows/{id}/validate · /dry-run | MEMBER | Valida y simula sin tocar LinkedIn. |
GET /executions · /executions/{id} | VIEWER | Ejecuciones y su detalle con KPIs por nodo. |
GET /prospect-executions/{id} | VIEWER | El recorrido de una persona, nodo a nodo. |
POST /executions/{id}/cancel | MANAGER | Detiene lo que quede pendiente. |
GET /approvals · POST /approvals/{id}/approve | VIEWER · MANAGER | Bandeja de aprobaciones desde fuera (Slack, por ejemplo). |
GET /dashboard/funnel · /stats · /activity | VIEWER | Los números del dashboard, para tus propios informes. |
GET /errors/unmanaged · /paused | VIEWER | Lo que espera decisión humana. Ideal para una alerta diaria. |
La receta que resuelve el 80 %: crear y disparar
Un prospecto nuevo entra en el CRM y, si lo dejas caer en una lista, el workflow que escucha esa lista arranca solo. Son dos llamadas, o incluso una:
curl -X POST https://app.linkedflow.pro/api/v1/prospects \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{
"linkedin_url": "https://www.linkedin.com/in/alguien",
"first_name": "Ana",
"company": "Consultora Norte",
"tags": ["n8n", "señal:contratando"],
"source": "n8n",
"list_id": "01a05d4e-23b2-725c-9aa4-e1ed9e8fd6da"
}'
¿De dónde sale el list_id? De la app: Prospectos → haz clic en la lista para seleccionarla → botón Copiar ID. Por API, GET /lists devuelve el id de cada una. Es un UUID (01a05d4e-23b2-725c-…); si mandas cualquier otra cosa, la respuesta es un 422 diciendo que list_id no es válido.
Tres cosas que conviene saber de esta llamada:
- Basta con
linkedin_urlopublic_identifier. Si la URL no es de un perfil, responde422. - El duplicado responde
409y no es un fallo: devuelve el prospecto que ya existía y lo añade igualmente a la lista. En n8n, configura el nodo para no abortar con 409 — es el camino normal cuando reprocesas una fuente. - Al entrar en la lista se dispara el trigger «Al añadir a una lista» de cualquier workflow activo que la escuche, tanto si el prospecto es nuevo como si ya existía y no estaba en ella. Ese es el enganche: n8n no necesita saber nada de workflows, solo llenar una lista.
Un list_id con forma de UUID pero que no existe (o que es de otro workspace) no da error: el prospecto se crea y no entra en ninguna lista. Es aislamiento entre inquilinos —la respuesta no puede servir para averiguar qué listas tiene otro—, así que comprueba el campo lists de la respuesta si quieres estar seguro de que entró donde querías.
Si prefieres lanzar tú explícitamente, usa POST /workflows/{id}/run con {"list_id":"…"} o {"prospect_ids":["…"]}. Requiere rol MANAGER y una versión guardada del workflow.
Webhook entrante: disparar un workflow desde fuera
Un workflow que empieza por el nodo Webhook expone una URL con token propio (visible en el builder, roles MANAGER o superior):
POST https://app.linkedflow.pro/api/v1/webhooks/workflows/{token}
Content-Type: application/json
{ "linkedin_url": "https://www.linkedin.com/in/alguien" }
Identificadores válidos, al menos uno: public_identifier, linkedin_url o email. Lo que mandes de más queda disponible dentro del recorrido como {{trigger.*}}, así que puedes pasar el motivo de la señal y usarlo en el texto del mensaje.
| Respuesta | Significa |
|---|---|
202 {"status":"accepted"} | Recibido. Siempre es esta, exista o no el prospecto y arranque o no el recorrido. |
404 | Token desconocido, workflow no activo, o su primer nodo no es un Webhook. |
422 | No mandaste ningún identificador. |
Este endpoint no crea prospectos. Resuelve uno que ya esté en tu CRM; si no lo encuentra, responde 202 igual y no pasa nada. Es deliberado: la respuesta no puede servir para averiguar quién está y quién no está en tu base. Si tu integración trae gente nueva, créala antes con POST /prospects.
El 202 tampoco garantiza que el recorrido empiece: un prospecto suprimido, marcado «no contactar» o que ya te respondió se queda fuera por las protecciones de siempre. Lo que pasó de verdad se ve en Ejecuciones.
Salidas: cuando LinkedFlow llama a tu herramienta
Webhook saliente
El nodo Webhook saliente hace un POST a la URL que le pongas. Si no defines cuerpo, manda este:
{
"prospect": {
"id": "01a0…",
"full_name": "Ana García",
"linkedin_url": "https://www.linkedin.com/in/…",
"company": "Consultora Norte",
"status": "REPLIED"
},
"execution_id": "01a0…",
"node_id": "webhook_1"
}
El POST no va firmado, y el nodo solo tiene dos campos: método y URL. No hay cabeceras ni cuerpo personalizable, así que no puedes mandar una firma ni un secreto en una cabecera. Todo lo que quieras que viaje contigo va en la URL, que sí admite variables:
https://tu-n8n/webhook/respuestas?s=UN-SECRETO-LARGO&cat={{previous.classification}}
En n8n eso llega como {{ $json.query.s }} y {{ $json.query.cat }} — no en headers ni en body. Comprueba el secreto con un nodo Si nada más entrar y descarta lo que no lo traiga: sin esa comprobación, cualquiera que descubra la URL puede inyectarte datos falsos.
Detalles operativos: 15 segundos de tiempo máximo; un 5xx se considera transitorio y se reintenta hasta tres veces con espera creciente; un 4xx se marca como fallo de configuración y espera decisión tuya en la bandeja de errores. Responde rápido y con 200: si tu automatización es lenta, acepta primero y procesa después.
Petición HTTP: pedir un dato y seguir
El nodo Petición HTTP es igual pero pensado para usar la respuesta. Lo que devuelvas queda disponible en el nodo siguiente:
{{previous.status}} → 200
{{previous.response.tier}} → "A" (tu JSON, bajo "response")
{{previous.response.angulo}} → "contratando SDR"
Con eso un nodo Si bifurca por tu propio scoring: {{previous.response.tier}} == "A". Devuelve siempre un JSON plano y responde en menos de 15 segundos; si tu servicio puede tardar, contesta un valor por defecto antes que hacer esperar al recorrido.
Cinco flujos de n8n que funcionan
1 · Señal externa → contacto (captar)
Schedule a las 7:30 → HTTP Request a tu fuente de señales (posts, cambios de puesto, ofertas de empleo) → Code que normaliza a linkedin_url → Remove Duplicates → HTTP Request a POST /prospects con list_id de «Señales de hoy» y la señal en tags.
En LinkedFlow: un workflow activo con disparador Al añadir a una lista → Leer perfil → Puntuar con IA → Si {{previous.score}} >= 70 → Reaccionar a su último post → Esperar 1 día → Enviar invitación con nota. Configura el nodo de n8n para tratar el 409 como éxito.
2 · Respuesta clasificada → CRM y agenda (atender)
En LinkedFlow: disparador Mensaje recibido → nodo Clasificar respuesta → todas las salidas al mismo Webhook saliente, con la categoría en la URL: ?cat={{previous.classification}}.
En n8n: Webhook → Si que compara {{ $json.query.s }} con tu secreto → Switch por {{ $json.query.cat }}: INTERESTED crea el negocio en tu CRM, avisa por Slack y manda el enlace de agenda; QUESTION redacta un borrador con IA y lo deja en Slack para aprobar; NOT_NOW escribe la fecha de recontacto en una hoja y un cron la reinyecta a los 90 días; DO_NOT_CONTACT llama a POST /suppression y cierra.
3 · «Comenta GUÍA y te la mando»
En LinkedFlow: disparador Comentario en un post con palabra clave → Reaccionar al comentario → Responder al comentario → Enviar mensaje con el enlace → Webhook saliente.
En n8n: alta en tu herramienta de email, fila en una hoja con comentario → entregado → abrió, y a los tres días, si no hubo respuesta, un POST al webhook entrante de un workflow de seguimiento. Recuerda que los comentarios se sondean cada 5 minutos y que responder a un comentario no gasta cupo de mensajes.
4 · Enriquecimiento y scoring propio a mitad de recorrido
Aquí es LinkedFlow quien llama a n8n. En el recorrido: Leer perfil → Petición HTTP a tu webhook de n8n (modo «responder con el último nodo») → Si {{previous.response.tier}} == "A" → invitación con nota personalizada; si no, a una lista de nurturing.
En n8n: Webhook → scrape de la web de la empresa → modelo de IA que devuelve JSON estricto (tier, angulo, dolor) → Respond to Webhook. Pon un fallback (tier: "B") para que un fallo tuyo no atasque el recorrido.
5 · El informe del lunes (vigilar)
Schedule lunes a las 7:00 → GET /dashboard/funnel y GET /executions → GET /errors/unmanaged → un modelo de IA con el prompt «tres decisiones, no tres gráficas» → email. Cero paneles nuevos: los números salen de la misma API que pinta el dashboard.
Descárgalos ya montados
Cuatro workflows de n8n listos para importar desde archivo. Antes de activarlos: crea una credencial Header Auth con nombre Authorization y valor Bearer <tu-token>, y sustituye el id de lista de ejemplo por el tuyo.
- 1 · Señal externa → prospecto en lista — el que resuelve el 80 %.
- 2 · Respuesta clasificada → CRM y agenda — incluye la comprobación de la cabecera secreta.
- 3 · Enriquecimiento y scoring — el que responde al nodo Petición HTTP.
- 4 · El informe del lunes — funnel, ejecuciones y errores por API.
Buenas prácticas
- Deduplica antes de llamar, por
linkedin_urly día. Dos POST iguales seguidos no duplican el prospecto, pero sí pueden meterlo dos veces en el flujo si lo sacas y lo vuelves a meter en la lista. - No pelees contra los cupos. Da igual lo rápido que empujes: las acciones salen al ritmo de la cuenta (25 visitas, 20 invitaciones y 30 mensajes al día por defecto). Encolar 500 prospectos no acelera nada, solo llena la cola.
- Registra el
execution_idque te llega en los webhooks salientes: es la llave para volver a mirar ese recorrido en la app. - Un usuario por integración, con rol mínimo, y rota su token si cambia de manos.
¿Te falta un endpoint? Escríbenos desde el chat de soporte de la app: la lista de lo que se documenta y se abre primero la marcan las integraciones que la gente está montando de verdad.