Integraciones Turiscool — guía + sandboxSIMULA UN HOST EXTERNO
Cada integración trae lo que hay que hacer en Turiscool y en tu plataforma (con el código fuente), y un probador en vivo al final que ejecuta el flujo real contra esta instancia.
①FUNDAE — botón embebible en tu LMS
Tu plataforma muestra un botón que abre el panel FUNDAE de Turiscool con la sesión del usuario ya iniciada (SSO por launch JWT).
En Turiscool (lo configura el equipo Turiscool)
- 1Se crea una integración LMS y se te entregan tu
client_idyclient_secret(este último solo se muestra una vez; se puede rotar). - 2Se elige el modo de acceso de la integración: JIT (auto-provisión) o estricto (solo usuarios dados de alta). Ver nota abajo.
En tu plataforma (lo implementas tú)
- 1Provisiona una vez la cuenta FUNDAE de cada empresa-cliente y guarda su
cuentaId:
# UNA sola vez por empresa-cliente: provisiona la cuenta FUNDAE (Basic Auth con tus credenciales).
curl -X POST https://desarrollo.turiscool.com/api/v1/integraciones-lms/integ/cuentas \
-u "$FUNDAE_CLIENT_ID:$FUNDAE_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"nombre":"Hotel Demo SL","cif":"B12345678","external_ref":"empresa-42"}'
# → { "cuentaId": 3, ... } Guarda cuentaId = FUNDAE_CUENTA_ID (idempotente por external_ref)- 1Crea un endpoint en tu backend que, tras autenticar al usuario, firme el launch JWT con tu
client_secrety devuelva{ url }. El secreto nunca sale de tu servidor:
// POST /api/fundae/launch — en TU backend, DESPUÉS de autenticar al usuario en tu plataforma.
// El client_secret vive solo en el servidor: NUNCA lo expongas al navegador.
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
app.post('/api/fundae/launch', requireAuth, (req, res) => {
const launch = jwt.sign(
{
cuentaFundaeId: Number(process.env.FUNDAE_CUENTA_ID),
user: { email: req.user.email, nombre: req.user.nombre, apellidos: req.user.apellidos },
returnUrl: 'https://mi-plataforma.com/dashboard',
},
process.env.FUNDAE_CLIENT_SECRET, // 🔒 secreto, server-side
{
algorithm: 'HS256',
issuer: process.env.FUNDAE_CLIENT_ID, // → iss
jwtid: crypto.randomUUID(), // → jti (anti-replay, 1 solo uso)
expiresIn: '110s', // el panel exige exp ≤ 120s
},
);
res.json({ url: 'https://fundaedesarrollo.turiscool.com/embed#t=' + encodeURIComponent(launch) });
});- 1Añade el script y el web component donde quieras el botón:
<!-- En la página de tu plataforma donde quieras el acceso FUNDAE -->
<script src="https://fundaedesarrollo.turiscool.com/embed.js" defer></script>
<turiscool-fundae
launch-endpoint="/api/fundae/launch"
mode="popup"
label="Gestión FUNDAE">
</turiscool-fundae>// (opcional) reacciona a los eventos del botón
const btn = document.querySelector('turiscool-fundae');
btn.addEventListener('fundae:open', e => console.log('abierto →', e.detail.url));
btn.addEventListener('fundae:close', e => console.log('cerrado →', e.detail.reason));
btn.addEventListener('fundae:error', e => console.error('error →', e.detail.error));mode="modal"): iframe a pantalla completa; requiere que el panel permita tu origen en frame-ancestors (cookies de terceros).Requisito: el alumno debe estar dado de alta
Al pulsar el botón, tu backend firma el launch con tu client_secret — esa firma es la autenticación. Aun así, el embed no crea cuentas al vuelo: el alumno debe existir ya en la cuenta FUNDAE. Si el correo no está dado de alta, el canje responde 403 USUARIO_NO_PROVISIONADO.
POST https://desarrollo.turiscool.com/api/v1/integraciones-lms/integ/cuentas/<cuentaId>/participantes (Basic Auth). Reenviar el mismo DNI actualiza, no duplica.Sin integración LMS activa o sin cuenta.
Esperando config…
②Nivimu — SSO + alta de empleados
Tu plataforma RRHH crea/actualiza empleados en Turiscool y genera enlaces de acceso sin contraseña.
En Turiscool (lo configura el equipo Turiscool)
- 1Se crea una integración SSO asociada a tu marca y se te entregan
client_idyclient_secret.
En tu plataforma (lo implementas tú)
- 1(Opcional) Consulta el organigrama para mapear tus sociedades/puestos a los valores exactos de Turiscool:
# (opcional) Mapea tu organigrama a los valores EXACTOS que espera Turiscool.
curl https://desarrollo.turiscool.com/api/v1/rrhh-integrations/nivimu/organization_chart \
-u "$SSO_CLIENT_ID:$SSO_CLIENT_SECRET"
# → { marca, sociedades[], establecimientos[], departamentos[], puestos[], nivel_estudios[], categoria_profesional[] }- 1Alta/actualización de empleados (upsert por correo, Basic Auth):
// 1) Alta/actualización de empleado (upsert por correo). Acepta uno o un array.
const auth = 'Basic ' + Buffer.from(
`${process.env.SSO_CLIENT_ID}:${process.env.SSO_CLIENT_SECRET}`
).toString('base64');
await fetch('https://desarrollo.turiscool.com/api/v1/rrhh-integrations/nivimu/createUpdateUsers', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: auth },
body: JSON.stringify({
correo: 'empleado@hotel.com',
nombre: 'Juan',
apellidos: 'Pérez Gómez',
dni: '12345678A',
sociedad: 'Hoteles Demo SL', // usa un nombre del organization_chart
}),
});
// → 201 { created:true, usuario_id } | 200 { created:false, usuario_id }- 1Genera el enlace SSO y redirige al empleado (válido 10 min):
# 2) Genera el enlace SSO y redirige al empleado (válido 10 min). NO requiere Basic Auth.
curl -X POST https://desarrollo.turiscool.com/api/v1/rrhh-integrations/auth/token \
-H "Content-Type: application/json" \
-d '{"client_secret":"'"$SSO_CLIENT_SECRET"'","email":"empleado@hotel.com"}'
# → { "url": ".../auth/sso/hr-redirect?client_id=...&token=..." }
# Redirige al empleado a esa url → entra sin contraseña.- 1(Opcional) Suspende o reactiva empleados (ajusta licencias):
# (opcional) Suspender / reactivar empleados (ajusta las licencias automáticamente).
curl -X POST https://desarrollo.turiscool.com/api/v1/rrhh-integrations/nivimu/updateUserStatus \
-u "$SSO_CLIENT_ID:$SSO_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"users":[{"email":"empleado@hotel.com","estado":"suspended"}]}'. + iniciales de nombre y apellidos. El enlace de /auth/token permite entrar sin contraseña.Sin integración SSO activa con secreto/sociedad resolubles.
Esperando config…