Importaciones
El importador puebla el padrón desde una sola planilla (CSV o XLSX) con estudiantes, apoderados, Cursos y Generación en las mismas filas. El flujo tiene tres pasos sin estado en el servidor — analyze detecta columnas y propone un mapeo, preview simula, commit escribe — y un contrato de checksum garantiza que los tres pasos hablan del mismo archivo. Todas las rutas exigen el módulo import del plan (si no está licenciado, 403 MODULE_NOT_LICENSED).
Los permisos se derivan del mapeo, no del archivo: subjects.write
siempre; si el mapeo incluye columnas de apoderado, además guardians.write;
si asigna Cursos o Generación, además students.write. Un mapeo que tu key no
puede ejecutar responde 403 AUTHORIZATION_REQUIRED con details.permission
indicando cuál falta.
El contrato de checksum
analyze calcula el SHA-256 del archivo y lo devuelve como analysis.fileChecksum, ya embebido en analysis.suggestedMapping. preview y commit reciben el archivo de nuevo junto con el mapeo, recalculan el hash y lo comparan con mapping.fileChecksum: si no coinciden, 422 IMPORT_FILE_CHANGED. No hay sesión de importación ni uploadId — el commit re-ejecuta todo el pipeline y nunca confía en un preview anterior; el checksum es la única (y suficiente) garantía de que no se comprometen bytes distintos de los analizados.
Analizar la planilla
Requiere subjects.write. multipart/form-data con un único campo file (CSV o XLSX, máximo 5 MB). Detecta delimitador y codificación, clasifica cada columna con su confianza, reconoce la forma de los apoderados (wide: columnas apoderado 1/2; long: una fila por vínculo) y detecta si los nombres de hoja parecen Cursos (1°A, 1°B). La joya es suggestedMapping: un mapeo completo listo para reenviar a preview.
Errores propios
- Nombre
IMPORT_FILE_TYPE_UNSUPPORTED- Tipo
- 400
- Descripción
Ni CSV ni XLSX.
- Nombre
IMPORT_FILE_TOO_LARGE- Tipo
- 400
- Descripción
Más de 5 MB.
Solicitud
curl -X POST "https://app.edugoverna.com/api/imports/analyze" \
-H "x-api-key: {tu_api_key}" \
-F "file=@matriculas-2026.xlsx"
Respuesta (recortada)
{
"analysis": {
"fileChecksum": "0f2a…64-hex…",
"kind": "xlsx",
"delimiter": null,
"encoding": "utf-8",
"sheets": [
{
"name": "1°A",
"rowCount": 33,
"headers": ["RUT", "Nombres", "Apellidos", "…"],
"suggestedInclude": true,
"sheetNameLooksLikeCurso": true
}
],
"columns": [
{
"sourceIndex": 0,
"sourceHeader": "RUT",
"suggestedTarget": "student.rut",
"confidence": "high",
"alternatives": []
}
],
"guardianShape": { "detected": "wide", "confidence": "high" },
"suggestedMapping": {
"fileChecksum": "0f2a…64-hex…",
"guardianShape": "wide",
"sheets": [{ "name": "1°A", "include": true }],
"columns": [{ "sourceIndex": 0, "sourceHeader": "RUT", "target": "student.rut" }],
"cursoSource": { "type": "sheetName" },
"generacion": { "type": "none" },
"defaults": { "relationshipType": "apoderado" }
},
"warnings": []
}
}
Previsualizar
multipart/form-data con file (los mismos bytes) y mapping (el ImportMapping como string JSON — normalmente el suggestedMapping, ajustado). Simula la importación completa sin escribir nada: totales de altas/actualizaciones/errores, el veredicto fila a fila, y el plan de Cursos — qué nombre de origen cae en qué segmento existente y cuáles habría que crear.
Para que commit pueda crear un Curso nuevo, su sourceName debe venir autorizado en mapping.approvedCursoCreations; si no, 422 IMPORT_CURSO_NOT_APPROVED. Así una hoja mal nombrada no acuña cursos por accidente.
Solicitud
curl -X POST "https://app.edugoverna.com/api/imports/preview" \
-H "x-api-key: {tu_api_key}" \
-F "file=@matriculas-2026.xlsx" \
-F 'mapping={"fileChecksum":"0f2a…", "guardianShape":"wide", …}'
Respuesta (recortada)
{
"preview": {
"totals": {
"students": { "new": 31, "update": 2, "error": 0 },
"guardians": { "new": 54, "update": 3, "error": 1 },
"guardianships": { "new": 55, "update": 0, "error": 1 }
},
"rows": [
{
"sheetName": "1°A",
"line": 2,
"status": "new",
"rut": "23456789-6",
"studentName": "Martina Rojas Fuentes",
"guardianNames": ["Carolina Fuentes"],
"cursoName": "1°A",
"warnings": []
}
],
"cursoPlan": [
{ "sourceName": "1°A", "resolution": { "type": "create" }, "studentCount": 33 }
],
"ignoredColumns": [],
"sheetErrors": []
}
}
Confirmar
Mismo cuerpo que preview más el campo opcional rowStart: el commit escribe en tramos de 40 filas y result.progress te dice si queda otro tramo por pedir. El resultado detalla fila a fila qué se creó, actualizó o falló, más los reportes de Cursos y Generación.
Solicitud
curl -X POST "https://app.edugoverna.com/api/imports/commit" \
-H "x-api-key: {tu_api_key}" \
-F "file=@matriculas-2026.xlsx" \
-F 'mapping={"fileChecksum":"0f2a…", …}' \
-F "rowStart=0"
Respuesta (recortada)
{
"result": {
"students": { "created": 31, "updated": 2, "failed": 0, "skipped": 0 },
"guardians": { "created": 54, "updated": 3, "failed": 1 },
"guardianships": { "created": 55, "updated": 0, "failed": 1 },
"rows": [
{
"sheetName": "1°A",
"line": 2,
"outcome": "created",
"subjectId": "3f6f…",
"rut": "23456789-6",
"warnings": []
}
],
"issues": [],
"cursoReport": [
{ "sourceName": "1°A", "segmentId": "5b2c…", "created": true, "added": 33 }
],
"progress": { "totalRows": 33, "rangeStart": 0, "rangeEnd": 33, "done": true }
}
}
Plantilla oficial
GET /imports/template.xlsx descarga la planilla de ejemplo (plantilla-estudiantes.xlsx). Está garantizado por contrato que la plantilla pasa por analyze sin advertencias y con todas sus columnas detectadas en confianza high — es el punto de partida recomendado para los colegios.
Padrones compartidos y registradores externos
Cuando quien tiene la nómina no es el colegio (un club, una academia, un fotógrafo), el padrón entra por registradores externos: se les envía una invitación y ellos cargan su registro en un portal público con el mismo importador. Desde la API de organización se administra la relación, no la carga:
| Método y ruta | Permiso | Qué hace |
|---|---|---|
GET /registrar-invites | registrar_invites.manage | Invitaciones enviadas, con estado (pending, expired, aceptadas con su registryOrganizationId). |
POST /registrar-invites | registrar_invites.manage | Invita a un registrador externo por correo. |
POST /registrar-invites/:inviteId/resend | registrar_invites.manage | Reenvía la invitación. |
DELETE /registrar-invites/:inviteId | registrar_invites.manage | Cancela la invitación. |
GET /registry-shares | registry_shares.read | Los padrones que tú compartiste como dueño: grupo, estado, memberCount, campañas y solicitudes asociadas; los compartidos contigo llegan por GET /shared-sources. |
POST /registry-shares · PATCH · DELETE | registry_shares.write | Otorga, ajusta o revoca un share (por Curso: N shares por registro). |
GET /shared-sources | shared_subjects.read | Las fuentes compartidas y sus titulares, para campañas sobre padrón ajeno. |
Las campañas de consentimiento sobre padrones compartidos están documentadas en Consentimientos y en la guía de campañas compartidas.