Ir al contenido

API — Ingesta de eventos

La API de ingesta recibe eventos de aprendizaje desde Moodle (u otros orígenes) y los encola para procesamiento. Es la superficie que usa el plugin Moodle, pero puedes enviar eventos directamente si tienes un integrador propio.

https://ingest.softsysanalytics.com

Todos los endpoints de ingesta (excepto /health) requieren la cabecera:

X-SSEA-Key: ssea_<32_hex_chars>

Las claves las provisiona SoftSys Solutions por tenant. Ver Seguridad y privacidad para detalles sobre formato, expiración y revocación.

Envía un único evento. Útil para integradores con bajo volumen o para pruebas.

Ventana de terminal
curl -X POST https://ingest.softsysanalytics.com/v1/events \
-H "X-SSEA-Key: ssea_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"event_type": "quiz_submitted",
"user_id": "moodle_user_42",
"course_id": "moodle_course_7",
"module_id": "moodle_cm_13",
"timestamp": "2026-04-13T14:30:00Z",
"metadata": {
"attempt_number": 2,
"score_percent": 84.5,
"time_taken_seconds": 1200
}
}'

Respuesta exitosa — 202 Accepted

{
"ok": true,
"data": {
"queued": 1,
"event_id": "<deterministic_hash>"
}
}

El código 202 indica que el evento fue aceptado y encolado, pero aún no procesado. El dashboard puede tardar unos minutos en reflejar eventos recién encolados.

Envía hasta 500 eventos en una sola request. Recomendado para integradores con alto volumen o para sincronizaciones iniciales.

Ventana de terminal
curl -X POST https://ingest.softsysanalytics.com/v1/events/batch \
-H "X-SSEA-Key: ssea_<your_key>" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"event_type": "course_viewed",
"user_id": "u1",
"course_id": "c1",
"timestamp": "2026-04-13T10:00:00Z"
},
{
"event_type": "resource_viewed",
"user_id": "u1",
"course_id": "c1",
"module_id": "m1",
"timestamp": "2026-04-13T10:01:00Z"
}
]
}'

Respuesta — 202 Accepted

{
"ok": true,
"data": {
"queued": 2,
"skipped": 0
}
}
  • queued: eventos aceptados y encolados.
  • skipped: eventos descartados por validación (formato incorrecto, tipos no soportados). No dispara error si al menos uno entra.

POST /v1/sync — Sincronización de entidades Moodle

Sección titulada «POST /v1/sync — Sincronización de entidades Moodle»

Usado por el plugin Moodle para migrar datos históricos (usuarios, cursos, matrículas, actividades, calificaciones) al backend de SSEA. No es para telemetría de eventos — usa /v1/events para eso.

Este endpoint está pensado para el plugin Moodle y su formato de payload está acoplado al flujo del plugin. Si construyes un integrador propio, probablemente lo que quieras es enviar eventos normales por /v1/events.

No requiere autenticación. Útil para monitoreo externo y pruebas de conectividad desde el servidor Moodle.

Ventana de terminal
curl https://ingest.softsysanalytics.com/health

Respuesta — 200 OK

{
"ok": true,
"service": "ssea-ingest"
}

El payload sigue un núcleo común con campos opcionales según el tipo de evento. Ver Tipos de eventos para el catálogo completo.

{
"event_type": "string (uno de los 15 tipos soportados)",
"user_id": "string (obligatorio — ID del usuario Moodle)",
"course_id": "string (opcional — ID del curso Moodle)",
"module_id": "string (opcional — ID del módulo del curso)",
"timestamp": "string (ISO 8601 UTC, obligatorio)",
"context": {
"object_id": "string (opcional)",
"context_level": "number (opcional)"
},
"metadata": "object (opcional — campos específicos del evento)",
"ip": "string (opcional — se hashea server-side antes de persistir)"
}

Notas:

  • event_type: debe ser uno del catálogo. Tipos desconocidos se descartan.
  • timestamp: cuándo ocurrió el evento en el origen (no cuándo lo envías). Formato ISO 8601 UTC con Z (2026-04-13T14:30:00Z).
  • ip: si la envías, el worker de ingesta calcula SHA-256(ip + salt) y descarta el valor crudo antes de encolar. Si prefieres no mandar IP, el plugin Moodle tiene una opción no_ip.
  • metadata: campos específicos enriquecidos por el plugin (p. ej. score_percent para quiz_submitted). Ver catálogo.
HTTPSignificadoAcción del cliente
202 AcceptedEvento(s) aceptados y encolados.Continuar normalmente.
400 Bad RequestJSON inválido o mal formado.Revisar el body del request. No reintentar sin corregir.
401 UnauthorizedClave API faltante, inválida o revocada.Verificar cabecera X-SSEA-Key. Si ya tenías claves válidas, contactar a soporte para confirmar estado.
402 Payment RequiredCuota diaria del plan excedida.Esperar al reset (medianoche UTC) o hacer upgrade de plan. Ver Planes y límites.
422 Unprocessable EntityValidación falló (p. ej. event_type obligatorio faltante, batch >500 eventos).Corregir el payload. No reintentar sin corregir.
429 Too Many RequestsRate limit excedido (ver cabecera Retry-After).Esperar el tiempo indicado y reintentar con backoff exponencial.
503 Service UnavailableWorker mal configurado (falta un secret crítico).Muy raro — contactar a soporte.

Todas las respuestas de error siguen el formato:

{
"ok": false,
"error": "Mensaje legible para humanos",
"code": "CODE_CONSTANT"
}

Códigos de error comunes:

codeHTTPSignificado
UNAUTHORIZED401API key inválida o expirada.
INVALID_JSON400JSON mal formado.
VALIDATION_ERROR422Campo obligatorio faltante o formato incorrecto.
BATCH_TOO_LARGE422Lote con más de 500 eventos.
PLAN_LIMIT_EXCEEDED402Cuota diaria del plan agotada.
RATE_LIMITED429Demasiados requests en la ventana actual.
  • Por defecto: 1 000 requests por minuto y por tenant.
  • Planes Enterprise: hasta 10 000 req/60s, negociado por contrato.
  • Las respuestas 429 incluyen la cabecera Retry-After: <segundos>.

La recomendación para clientes es implementar backoff exponencial: reintentar tras 1 s, 2 s, 4 s, 8 s… con un máximo razonable (p. ej. 5 reintentos). El plugin Moodle ya lo hace automáticamente.

  • Cada evento genera un ID determinístico basado en el hash de sus campos clave.
  • Si reenvías el mismo evento (por ejemplo, por un timeout de red seguido de retry), el worker de procesamiento detecta el duplicado y no lo cuenta dos veces.
  • Esto significa que puedes reintentar requests 202 con seguridad: no duplicas datos.