Cursos personalizados
Un curso personalizado es una secuencia curada y orientada al estudiante de temas y escenarios extraídos del catálogo de contenido de SecureCodingHub. Una vez creado, un curso personalizado se puede asignar igual que un tema o categoría integrados. Requiere custom-courses:read o custom-courses:write según el verbo.
Endpoints
| Método | Ruta | Scope | Propósito |
|---|---|---|---|
GET | /custom-courses | custom-courses:read | Listar todos los cursos personalizados de la organización. |
GET | /custom-courses/{'{'}id{'}'} | custom-courses:read | Recuperar un único curso personalizado con sus elementos ordenados. |
POST | /custom-courses | custom-courses:write | Crear un nuevo curso personalizado. |
PATCH | /custom-courses/{'{'}id{'}'} | custom-courses:write | Actualizar metadatos y/o reemplazar la lista de elementos. |
DELETE | /custom-courses/{'{'}id{'}'} | custom-courses:write | Desactivar de forma suave un curso personalizado. |
Todas las rutas son relativas a https://api.limeplate.com/api/public/v1.
El modelo de elemento
Cada curso personalizado contiene una lista ordenada de items. Cada elemento es una referencia a un tema o escenario del catálogo: consulta Catálogo de contenido para los identificadores.
| Campo | Tipo | Significado |
|---|---|---|
itemType | string | Bien "topic" (un tema de práctica como sql-injection) o "scenario" (un escenario en modo aprendizaje). |
itemId | string | El id del tema o escenario en el catálogo. |
orderIndex | integer | Orden de visualización en base cero en la UI del estudiante. Los valores menores aparecen primero. |
En PATCH, omite el campo items para dejar los elementos intactos, o envía una lista de reemplazo completa para sobrescribir. No hay actualización parcial de elementos: reenvía la secuencia completa en el orden en que quieras que quede guardada.
Listar cursos personalizados
Devuelve una entrada por cada curso personalizado activo en la organización, ordenadas por actualización más reciente.
GET /api/public/v1/custom-courses
Authorization: Bearer scs_live_…Respuesta
[
{
"id": "8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02",
"name": "Backend Onboarding — Q2",
"description": "Required reading for new backend hires.",
"icon": "shield",
"color": "#3b82f6",
"itemCount": 6,
"usageCount": 12,
"createdByUserId": "1a2b3c4d-5e6f-7081-9203-4d5e6f708192",
"createdByName": "Jane Smith",
"createdAt": "2026-04-10T09:22:14Z",
"updatedAt": "2026-05-18T15:01:47Z"
}
]Ejemplo
curl -sS \
-H "Authorization: Bearer $SCH_API_KEY" \
https://api.limeplate.com/api/public/v1/custom-coursesObtener un curso personalizado
Devuelve el curso con sus elementos ordenados expandidos. resolvedTitle es el título legible del tema o escenario subyacente en el momento de la lectura, cómodo para renderizar sin una segunda consulta al catálogo.
GET /api/public/v1/custom-courses/{customCourseId}
Authorization: Bearer scs_live_…Respuesta
{
"id": "8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02",
"name": "Backend Onboarding — Q2",
"description": "Required reading for new backend hires.",
"icon": "shield",
"color": "#3b82f6",
"items": [
{
"id": "c1d2e3f4-5060-7081-9203-4d5e6f708192",
"itemType": "topic",
"itemId": "sql-injection",
"resolvedTitle": "SQL Injection",
"orderIndex": 0
},
{
"id": "d2e3f405-6070-8192-0304-5e6f70819203",
"itemType": "scenario",
"itemId": "auth-bypass-walkthrough",
"resolvedTitle": "Auth Bypass Walkthrough",
"orderIndex": 1
},
{
"id": "e3f40506-7081-9203-0405-6f7081920304",
"itemType": "topic",
"itemId": "xss",
"resolvedTitle": "Cross-Site Scripting",
"orderIndex": 2
}
],
"usageCount": 12,
"createdByUserId": "1a2b3c4d-5e6f-7081-9203-4d5e6f708192",
"createdByName": "Jane Smith",
"createdAt": "2026-04-10T09:22:14Z",
"updatedAt": "2026-05-18T15:01:47Z"
}Ejemplo
curl -sS \
-H "Authorization: Bearer $SCH_API_KEY" \
https://api.limeplate.com/api/public/v1/custom-courses/8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02Crear un curso personalizado
Crea un curso y devuelve la vista de detalle. La organización de la API key es propietaria del nuevo curso; el usuario que emitió la clave queda registrado como creador.
POST /api/public/v1/custom-courses
Authorization: Bearer scs_live_…
Content-Type: application/jsonCuerpo de la solicitud
{
"name": "Backend Onboarding — Q2",
"description": "Required reading for new backend hires.",
"icon": "shield",
"color": "#3b82f6",
"items": [
{ "itemType": "topic", "itemId": "sql-injection", "orderIndex": 0 },
{ "itemType": "scenario", "itemId": "auth-bypass-walkthrough", "orderIndex": 1 },
{ "itemType": "topic", "itemId": "xss", "orderIndex": 2 }
]
}name es obligatorio y debe ser único dentro de la organización. description, icon y color son opcionales: color admite cualquier cadena hexadecimal CSS. items puede estar vacío en la creación; puedes rellenarlo después vía PATCH.
Respuesta
Misma forma que Obtener un curso personalizado.
Ejemplo
curl -sS -X POST \
-H "Authorization: Bearer $SCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Backend Onboarding — Q2",
"description": "Required reading for new backend hires.",
"icon": "shield",
"color": "#3b82f6",
"items": [
{ "itemType": "topic", "itemId": "sql-injection", "orderIndex": 0 },
{ "itemType": "topic", "itemId": "xss", "orderIndex": 1 }
]
}' \
https://api.limeplate.com/api/public/v1/custom-coursesActualizar un curso personalizado
Todos los campos de la solicitud son opcionales. Pasa solo los campos que quieras cambiar. Enviar items reemplaza toda la lista de elementos: para reordenar, reenvía cada elemento con nuevos valores de orderIndex.
PATCH /api/public/v1/custom-courses/{customCourseId}
Authorization: Bearer scs_live_…
Content-Type: application/jsonCuerpo de la solicitud
{
"name": "Backend Onboarding — Q3",
"items": [
{ "itemType": "topic", "itemId": "sql-injection", "orderIndex": 0 },
{ "itemType": "topic", "itemId": "ssrf", "orderIndex": 1 },
{ "itemType": "scenario", "itemId": "jwt-tampering", "orderIndex": 2 }
]
}Respuesta
El objeto de detalle actualizado.
Ejemplo
curl -sS -X PATCH \
-H "Authorization: Bearer $SCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Backend Onboarding — Q3" }' \
https://api.limeplate.com/api/public/v1/custom-courses/8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02Eliminar un curso personalizado
Desactiva el curso de forma suave. Deja de aparecer en GET /custom-courses y en las UIs de estudiante. Las asignaciones existentes que referenciaban el curso siguen registrando progreso histórico, pero ya no se entregan a nuevos destinatarios.
DELETE /api/public/v1/custom-courses/{customCourseId}
Authorization: Bearer scs_live_…Respuesta
{ "message": "Custom course deactivated" }Ejemplo
curl -sS -X DELETE \
-H "Authorization: Bearer $SCH_API_KEY" \
https://api.limeplate.com/api/public/v1/custom-courses/8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02Asignar un curso personalizado
Un curso personalizado no se entrega a los estudiantes hasta que se asigna. Usa Asignaciones con targetType: "custom-course" y targetId apuntando al id del curso personalizado:
curl -sS -X POST \
-H "Authorization: Bearer $SCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"assigneeType": "user",
"assigneeId": "1a2b3c4d-5e6f-7081-9203-4d5e6f708192",
"contentArea": "practice",
"targetType": "custom-course",
"targetId": "8b1f0c2e-3a45-4d99-9a7e-2c5e1b3a8f02",
"deadline": "2026-06-15T00:00:00Z",
"isMandatory": true
}' \
https://api.limeplate.com/api/public/v1/assignmentsEl estudiante ve los elementos en el orden de orderIndex. Los temas de práctica y los escenarios de aprendizaje se rastrean bajo sus respectivos endpoints de progreso: consulta Progreso.