Saltar al contenido

Formación · API

Para tu equipo técnico

La formación, conectada a tus programas.

Da de alta personas desde tu programa de nóminas, convoca cursos desde tu portal del empleado y recibe un aviso firmado cada vez que alguien termina un curso o recibe su certificado.

Empezar

Autenticación

Crea una clave en el panel de formación, pestaña Integraciones. Cada clave es de tu empresa, se enseña una sola vez y puede ser de solo lectura o de lectura y escritura. Envíala en cada petición:

curl https://galaworklab.com/api/formacion/v1/espacio \
  -H "Authorization: Bearer gwk_XXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

La API es para servidores: no admite llamadas desde el navegador (sin CORS). Guarda la clave como secreto en tu programa, nunca en una página web. Si se filtra, retírala en el panel: deja de funcionar al instante.

Todo va en JSON (UTF-8). Las fechas son AAAA-MM-DD y las marcas de tiempo, ISO 8601 en UTC.

Límites

120peticiones por minuto y clave
500resultados por página, como máximo
300personas por alta en bloque

Cada respuesta lleva X-RateLimit-Limit y X-RateLimit-Remaining. Si te pasas, recibes 429: espera al minuto siguiente.

Paginación

Los listados devuelven { "datos": [...], "siguiente": "…" }. Pide la página siguiente con ?despues= y el valor de siguiente; cuando es null, has llegado al final. Tamaño con ?limite= (100 por defecto).

curl "https://galaworklab.com/api/formacion/v1/personas?limite=200&despues=alu_8f2c…" \
  -H "Authorization: Bearer $GALA_CLAVE"

Errores

Los errores devuelven { "error": "mensaje en español" } con su código HTTP.

CódigoQué significa
400Faltan datos o no son válidos. El mensaje dice cuál.
401Falta la clave, no es válida o se ha retirado.
402No quedan plazas compradas para ese curso.
403La clave es de solo lectura y has intentado escribir.
404No existe o no es de tu empresa.
429Has superado el límite por minuto.

Recursos

Espacio

GET/api/formacion/v1/espacio

Los datos de tu empresa. Útil para comprobar que la clave funciona.

{ "id": "esp_…", "empresa": "Construcciones Norte, S.L.", "cif": "B12345678" }

Personas

GET/api/formacion/v1/personas
GET/api/formacion/v1/personas/{id}

La plantilla en activo: id, nombre, documento, correo, puesto, centro y creado.

POST/api/formacion/v1/personas · escritura

Da de alta una persona o hasta 300 de golpe con { "personas": [...] }. Solo es obligatorio el nombre y apellidos. Devuelve el enlace personal de cada una para que se lo hagas llegar, y se aplican tus reglas de grupos.

curl -X POST https://galaworklab.com/api/formacion/v1/personas \
  -H "Authorization: Bearer $GALA_CLAVE" -H "Content-Type: application/json" \
  -d '{ "personas": [
        { "nombre": "Lucía Martín Ruiz", "documento": "12345678Z", "correo": "[email protected]", "puesto": "Encargada", "centro": "Bilbao" }
      ] }'

# 201 Created
{ "datos": [ { "id": "alu_…", "nombre": "Lucía Martín Ruiz", "enlace": "https://galaworklab.com/empresas/formacion/curso/#i=…" } ] }
DELETE/api/formacion/v1/personas/{id} · escritura

Da de baja a la persona. Su historial y sus certificados se conservan.

Cursos

GET/api/formacion/v1/cursos

Los cursos que puedes convocar: los del catálogo con plazas vigentes y los propios publicados (propio: true). Cada uno con id, titulo, horas y norma.

Convocatorias

GET/api/formacion/v1/convocatorias
GET/api/formacion/v1/convocatorias/{id}

Cada convocatoria con su curso, fechas y número de personas inscritas.

POST/api/formacion/v1/convocatorias · escritura

Convoca un curso a personas sueltas, a grupos o a ambos. Con "invitar": true enviamos la invitación por correo a quien lo tenga. Consume plazas: si no hay, devuelve 402.

curl -X POST https://galaworklab.com/api/formacion/v1/convocatorias \
  -H "Authorization: Bearer $GALA_CLAVE" -H "Content-Type: application/json" \
  -d '{ "curso": "acoso", "inicio": "2026-11-02", "fin": "2026-11-30",
        "personas": ["alu_…"], "grupos": ["gru_…"], "nombre": "Acoso · noviembre", "invitar": true }'

# 201 Created
{ "id": "con_…", "inscritas": 24, "plazas_restantes": 76 }

Inscripciones

GET/api/formacion/v1/inscripciones

El progreso de cada persona en cada convocatoria: estado, nota, intentos, inicio y fin. Filtra con ?convocatoria=, ?persona= o ?estado= (pendiente, en-curso, apto, no-apto).

Certificados

GET/api/formacion/v1/certificados
GET/api/formacion/v1/certificados/{codigo}

Los certificados emitidos, con la dirección pública para verificarlos. Con ?desde=AAAA-MM-DD traes solo los terminados desde esa fecha: lo normal para sincronizar cada noche con tu expediente de personal.

{ "codigo": "GW-7K2M-…", "persona": "alu_…", "nombre": "Lucía Martín Ruiz", "titulo": "Prevención del acoso laboral",
  "horas": 2, "nota": 90, "fin": "2026-11-12", "anulado": null,
  "verificar": "https://galaworklab.com/empresas/formacion/verificar/?c=GW-7K2M-…" }

Avisos

Webhooks

Da de alta en el panel (pestaña Integraciones) la dirección https de tu programa y elige los eventos. Te enviamos un POST en JSON en el momento en que ocurren. Responde con un código 2xx en menos de 5 segundos; si no, reintentamos al minuto, a los 5 y 30 minutos, a las 2 horas y a las 12 horas.

EventoCuándo
inscripcion.creadaAl convocar a una o varias personas (un aviso por convocatoria, con todas las inscripciones nuevas).
curso.terminadoCuando alguien aprueba o agota los intentos (resultado: apto o no-apto).
certificado.emitidoCuando se genera el certificado, con su código y su dirección de verificación.

Cabeceras: Gala-Evento, Gala-Entrega (identificador único: úsalo para no procesar dos veces el mismo aviso) y Gala-Firma.

{
  "id": "evt_…",
  "tipo": "certificado.emitido",
  "creado": "2026-11-12T09:41:07Z",
  "espacio": "esp_…",
  "datos": {
    "inscripcion": "ins_…", "convocatoria": "con_…", "curso": "acoso", "version": 3,
    "persona": { "id": "alu_…", "nombre": "Lucía Martín Ruiz", "correo": "[email protected]" },
    "codigo": "GW-7K2M-…", "titulo": "Prevención del acoso laboral", "horas": 2, "nota": 90, "fin": "2026-11-12",
    "verificar": "https://galaworklab.com/empresas/formacion/verificar/?c=GW-7K2M-…"
  }
}

Comprobar la firma

Cada aviso lleva Gala-Firma: t=1762940467,v1=5f3a…. Calcula el HMAC-SHA256 en hexadecimal de t + "." + cuerpo (el cuerpo tal cual llega, sin volver a formatearlo) con tu secreto whsec_…, compáralo en tiempo constante con v1 y rechaza avisos con t de hace más de 5 minutos.

Node.js

import crypto from 'node:crypto';

export function avisoValido(cuerpo, cabecera, secreto) {
  const { t, v1 } = Object.fromEntries(cabecera.split(',').map((p) => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const esperada = crypto.createHmac('sha256', secreto).update(t + '.' + cuerpo).digest('hex');
  return v1?.length === esperada.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(esperada));
}

Python

import hmac, hashlib, time

def aviso_valido(cuerpo: bytes, cabecera: str, secreto: str) -> bool:
    partes = dict(p.split("=", 1) for p in cabecera.split(","))
    t, v1 = partes.get("t", "0"), partes.get("v1", "")
    if abs(time.time() - int(t)) > 300:
        return False
    esperada = hmac.new(secreto.encode(), f"{t}.".encode() + cuerpo, hashlib.sha256).hexdigest()
    return hmac.compare_digest(v1, esperada)

¿Has perdido el secreto o crees que se ha filtrado? Pulsa Nuevo secreto en el panel: el anterior deja de valer al momento.