---
title: "Sujetos del padrón"
description: "Titulares, apoderados, Generaciones y Cursos por API: proyección de lectura por tiers, listados no sensibles y el registro completo sólo en el detalle."
url: "https://docs.edugoverna.com/api-organizacion/sujetos"
---
# Sujetos del padrón

El padrón es el corazón del producto: cada persona cuyos datos trata el colegio es un **titular** (`data_subject`) — estudiante, apoderado, funcionario u otro. Alrededor del titular orbitan los vínculos de **apoderado** (`guardianship`), las **Generaciones** (`student_cohort`, la promoción que avanza junta) y los **Cursos y segmentos** (`student_segment`; un Curso es un segmento con `kind: "course"`). Esta página cubre las cinco superficies, empezando por la regla que las gobierna a todas: la proyección de lectura. {{ className: 'lead' }}

La vista de producto del padrón está en [Padrón](/padron).

## Qué ves depende de tu proyección

Cada respuesta de lectura de titulares se filtra por la [escalera de permisos `subjects.read.*`](/api-organizacion/introduccion#la-escalera-de-lectura-de-titulares) de tu API key, y la proyección aplicada viaja en `meta.projection` para que tu integración no tenga que adivinar:

- **Los listados nunca son sensibles.** `GET /data-subjects` degrada la proyección al nivel de nómina: identidad a lo más `basic` (nombres sí, fecha de nacimiento/dirección/RUT no) y contacto a lo más `masked`. Por eso listar no exige aprobación de lectura masiva ni escribe eventos de acceso sensible.
- **El registro completo vive en el detalle.** Sólo `GET /data-subjects/:subjectId` entrega la proyección plena de tu key; una lectura con proyección sensible queda registrada en el historial de accesos.
- **`directory` no entrega ningún nombre.** A ese tier los campos de identidad llegan en `null` y el titular se referencia por `referenceCode`.

Los campos que la proyección oculta llegan como `null`, nunca desaparecen del cuerpo: la forma de la respuesta es estable entre tiers.

---

## Listar titulares {{ tag: 'GET', label: '/api/data-subjects' }}

<Row>
  <Col>

    Requiere cualquier permiso `subjects.read.*`. Pagina por cursor y filtra por tipo y ciclo de vida; los titulares suprimidos o anonimizados no aparecen salvo que pidas `lifecycle=suppressed`.

    ### Parámetros opcionales

    <Properties>
      <Property name="type" type="string">
        Uno o más tipos separados por coma: `student`, `guardian`, `staff`,
        `other` (p. ej. `type=staff,other`).
      </Property>
      <Property name="q" type="string">
        Búsqueda: código de referencia, RUT o contacto exacto; nombres sólo si
        tu proyección es al menos `identity_basic`.
      </Property>
      <Property name="lifecycle" type="string">
        `suppressed` lista únicamente titulares suprimidos/anonimizados.
      </Property>
      <Property name="limit / cursor" type="varios">
        Paginación por cursor; `meta.nextCursor` indica la página siguiente.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/data-subjects">

    ```bash {{ title: 'cURL' }}
    curl -G "https://app.edugoverna.com/api/data-subjects" \
      -H "x-api-key: {tu_api_key}" \
      -d type=student -d limit=50
    ```

    ```js
    const { dataSubjects, meta } = await fetch(
      'https://app.edugoverna.com/api/data-subjects?type=student&limit=50',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "dataSubjects": [
        {
          "id": "3f6f0a2e-…",
          "subjectType": "student",
          "referenceCode": "EST-2026-0412",
          "status": "active",
          "currentAgeBand": "minor_14_17",
          "currentGeneralConsentAuthority": "guardian",
          "activeGuardianCount": 2,
          "legalGivenNames": "Martina",
          "legalFamilyNames": "Rojas Fuentes",
          "preferredName": null,
          "birthDate": null,
          "contactType": "email",
          "contactValue": null,
          "contactMaskedValue": "a•••o@example.com",
          "identifierType": "rut",
          "identifierValue": null,
          "identifierLast4": "6789",
          "studentGroupSummary": {
            "activeCohort": { "name": "Generación 2030" },
            "segments": [{ "name": "1°A" }]
          }
          // …
        }
      ],
      "meta": {
        "filteredRestrictedCount": 0,
        "totalMatchedCount": 412,
        "nextCursor": "eyJz…",
        "projection": {
          "identity": "basic",
          "contact": "masked",
          "identifier": "metadata",
          "studentProfile": "redacted",
          "guardianProfile": "redacted"
        }
      }
    }
    ```

  </Col>
</Row>

El detalle (`GET /data-subjects/:subjectId`) responde `{ dataSubject, meta }` con la misma forma pero bajo la proyección completa de tu key: con `identity_sensitive` aparecen `birthDate`, dirección y el RUT en claro; con `contact_full`, los valores de contacto descifrados en `contacts[]` y `primaryContact`.

---

## Crear un titular {{ tag: 'POST', label: '/api/data-subjects' }}

<Row>
  <Col>

    Requiere `subjects.write`. La creación es idempotente si envías un `id` generado por el cliente: reenviar el mismo payload devuelve el registro existente en vez de duplicarlo.

    ### Atributos requeridos

    <Properties>
      <Property name="subjectType" type="string">
        `student`, `guardian`, `staff` u `other`.
      </Property>
    </Properties>

    ### Atributos opcionales frecuentes

    <Properties>
      <Property name="legalGivenNames / legalFamilyNames" type="string">
        Identidad legal; `preferredName` para el nombre social.
      </Property>
      <Property name="birthDate" type="string">
        `YYYY-MM-DD`; alimenta la banda etaria y con ella la autoridad de
        consentimiento. Una fecha imposible responde
        `422 SUBJECT_BIRTH_DATE_INVALID`.
      </Property>
      <Property name="identifier" type="object">
        `{ identifierType, value, last4?, isPrimary? }` — típicamente el RUT.
        `identifierType` se normaliza a minúsculas al escribir (`"RUT"` y
        `"rut"` son el mismo tipo); para `rut` el valor se valida con dígito
        verificador y se canonicaliza antes de indexarse.
      </Property>
      <Property name="contacts" type="array">
        Hasta 4 contactos `{ contactType, value, isNotificationEnabled? }`
        (`email`, `phone`, `sms`, `other`); `primaryContactType` designa el
        primario. Un correo mal formado responde
        `422 SUBJECT_CONTACT_VALUE_INVALID`.
      </Property>
      <Property name="studentProfile / guardianProfile" type="object">
        Perfil según el tipo: código escolar, nivel, alertas; o relación y
        prioridad de emergencia.
      </Property>
      <Property name="id" type="string">
        UUID del cliente para idempotencia.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="POST" label="/api/data-subjects">

    ```bash {{ title: 'cURL' }}
    curl -X POST "https://app.edugoverna.com/api/data-subjects" \
      -H "x-api-key: {tu_api_key}" \
      -H "Content-Type: application/json" \
      -d '{
        "subjectType": "student",
        "legalGivenNames": "Martina",
        "legalFamilyNames": "Rojas Fuentes",
        "birthDate": "2012-04-18",
        "identifier": { "identifierType": "rut", "value": "23456789-6", "isPrimary": true },
        "contacts": [{ "contactType": "email", "value": "apoderado@example.com" }],
        "primaryContactType": "email"
      }'
    ```

    ```js
    const subject = await fetch(
      'https://app.edugoverna.com/api/data-subjects',
      {
        method: 'POST',
        headers: {
          'x-api-key': process.env.EDUGOVERNA_API_KEY,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          subjectType: 'student',
          legalGivenNames: 'Martina',
          legalFamilyNames: 'Rojas Fuentes',
          birthDate: '2012-04-18',
          identifier: {
            identifierType: 'rut',
            value: '23456789-6',
            isPrimary: true,
          },
          contacts: [
            { contactType: 'email', value: 'apoderado@example.com' },
          ],
          primaryContactType: 'email',
        }),
      },
    ).then((r) => r.json())
    ```

    </CodeGroup>

  </Col>
</Row>

---

## El resto del ciclo de vida del titular

| Método y ruta                                     | Permiso           | Qué hace                                                                                                                    |
| ------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `PATCH /data-subjects/:id/identity`               | `subjects.write`  | Rectifica nombres y fecha de nacimiento (recalcula la banda etaria).                                                        |
| `PATCH /data-subjects/:id/contacts`               | `subjects.write`  | Reemplaza el set de contactos y designa el primario.                                                                        |
| `PATCH /data-subjects/:id/contact`                | `subjects.write`  | Atajo legado: actualiza sólo el contacto primario.                                                                          |
| `PATCH /data-subjects/:id/lifecycle`              | `subjects.write`  | `suppress` (excluye de todo proceso operativo) o `reactivate`.                                                              |
| `GET /data-subjects/:id/verifications`            | `subjects.read.*` | Verificaciones de identidad del titular (referencias, no bytes).                                                            |
| `GET /data-subjects/:id/restrictions`             | `subjects.read.*` | Restricciones de tratamiento activas.                                                                                       |
| `GET /data-subjects/:id/anonymization-approvals`  | `subjects.write`  | Estado de la aprobación de anonimización abierta.                                                                           |
| `POST /data-subjects/:id/anonymization-approvals` | `subjects.write`  | Abre la solicitud de anonimización a cuatro ojos (responde `202`). **Requiere sesión**: la ejecuta otra persona al aprobar. |

<Note>
  **Sólo disponible con sesión de la consola:** la purga de un registro creado
  por error (`GET /data-subjects/:id/purge-eligibility` y `DELETE
  /data-subjects/:id`, permiso `subjects.purge`). Destruir una fila exige una
  persona identificable, y el servidor rechaza purgar cualquier titular con
  historial de consentimientos, ARCO o incidentes.
</Note>

---

## Apoderados {{ tag: 'GET', label: '/api/guardianships' }}

<Row>
  <Col>

    El vínculo estudiante ↔ apoderado, con su autoridad de consentimiento. Lectura con `guardians.read` (filtros `studentSubjectId` y `guardianSubjectId`); creación y edición con `guardians.write`.

    Campos clave del vínculo: `relationshipType`, `authorityScope` (`full` por defecto), `canConsentGeneral`, `canConsentSensitive`, `canExerciseRights`, `priority`, `status`, `validFrom`/`validTo` y `provenance` — `administrative` (creado por el colegio) o `self_attested` (declarado por el apoderado en una campaña pública, provisional hasta que se verifique su documento de autoridad).

    `POST /guardianships` requiere `studentSubjectId`, `guardianSubjectId` y `relationshipType`; responde `201` con la fila creada. Cada elemento del listado agrega `studentSummary`, `guardianSummary` y `studentGroupSummary`.

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/guardianships">

    ```bash {{ title: 'cURL' }}
    curl -G "https://app.edugoverna.com/api/guardianships" \
      -H "x-api-key: {tu_api_key}" \
      -d studentSubjectId=3f6f0a2e-…
    ```

    ```js
    const { guardianships } = await fetch(
      'https://app.edugoverna.com/api/guardianships?studentSubjectId=3f6f0a2e-…',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "guardianships": [
        {
          "id": "gd12…",
          "studentSubjectId": "3f6f0a2e-…",
          "guardianSubjectId": "8a4c…",
          "relationshipType": "madre",
          "authorityScope": "full",
          "canConsentGeneral": true,
          "canConsentSensitive": true,
          "canExerciseRights": true,
          "priority": 1,
          "status": "active",
          "provenance": "administrative",
          "studentSummary": { "referenceCode": "EST-2026-0412", "displayName": "Martina Rojas" },
          "guardianSummary": { "referenceCode": "APO-2026-0311", "displayName": "Carolina Fuentes" }
        }
      ],
      "meta": { "filteredRestrictedCount": 0 }
    }
    ```

  </Col>
</Row>

Los documentos que acreditan la autoridad de un apoderado (resoluciones, poderes) se gestionan en [`/records/authority-documents`](/api-organizacion/gobernanza).

---

## Generaciones {{ tag: 'GET', label: '/api/student-cohorts' }}

La Generación es la promoción que avanza junta por los años escolares. Lectura con `students.read`, escritura con `students.write`.

| Método y ruta                                                 | Permiso          | Qué hace                                                                                                                           |
| ------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `GET /student-cohorts`                                        | `students.read`  | Lista `{ studentCohorts }`: cada una con `name`, `anchorSchoolYear`, `expectedGraduationYear`, curso actual y `activeMemberCount`. |
| `POST /student-cohorts`                                       | `students.write` | Crea una Generación (`name` y `anchorSchoolYear` requeridos); `409 STUDENT_COHORT_CODE_CONFLICT` si el código choca.               |
| `GET /student-cohorts/:cohortId`                              | `students.read`  | Detalle.                                                                                                                           |
| `PATCH /student-cohorts/:cohortId`                            | `students.write` | Actualiza año, curso o estado.                                                                                                     |
| `POST /student-cohorts/:cohortId/members`                     | `students.write` | Matricula un estudiante.                                                                                                           |
| `POST /student-cohorts/:cohortId/members/bulk`                | `students.write` | Matrícula masiva; responde un reporte por fila.                                                                                    |
| `DELETE /student-cohorts/:cohortId/members/:studentSubjectId` | `students.write` | Retira un estudiante.                                                                                                              |

---

## Cursos y segmentos {{ tag: 'GET', label: '/api/student-segments' }}

<Row>
  <Col>

    Un segmento agrupa estudiantes; un **Curso es un segmento con `kind: "course"`**, que además porta `courseSchoolYear`, `courseGradeLevel` y `courseCode`. Los segmentos pueden ser de membresía fija (`type: "fixed"`) o dinámicos por filtro (`type: "dynamic"`); un Curso sólo admite membresía fija.

    Lectura con `students.read`; el parámetro `kind=course` filtra los Cursos. Escritura con `students.write` (mismas rutas de miembros que las Generaciones). Dos particularidades:

    - `POST /student-segments/preview` simula un `filterDefinition` antes de crear el segmento (muestra hasta 50 estudiantes). Filtrar por `hasSpecialNeeds` o `hasHealthAlerts` exige además `subjects.read.identity_sensitive` (`403 SUBJECT_SENSITIVE_READ_REQUIRED`).
    - `GET /student-segments/:segmentId` responde `{ studentSegment, members, meta }`; cada miembro trae un `studentSummary` de nómina (nombre y código de referencia), no el registro completo del titular.

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/student-segments">

    ```bash {{ title: 'cURL' }}
    curl -G "https://app.edugoverna.com/api/student-segments" \
      -H "x-api-key: {tu_api_key}" \
      -d kind=course
    ```

    ```js
    const { studentSegments } = await fetch(
      'https://app.edugoverna.com/api/student-segments?kind=course',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "studentSegments": [
        {
          "id": "5b2c…",
          "name": "1°A",
          "kind": "course",
          "type": "fixed",
          "courseSchoolYear": 2026,
          "courseGradeLevel": "1° básico",
          "courseCode": "1A",
          "status": "active",
          "activeMemberCount": 32,
          "filterDefinition": null
        }
      ]
    }
    ```

  </Col>
</Row>

---

## Perfil del colegio {{ tag: 'GET', label: '/api/schools/current' }}

La organización dueña de la API key, con su perfil escolar. Lectura con `schools.read`; `POST /schools/current` (con `schools.write`) crea o actualiza el perfil (`201` la primera vez, `200` después).

```json {{ title: 'Respuesta' }}
{
  "organization": {
    "id": "org_…",
    "name": "Colegio Andes",
    "slug": "colegio-andes",
    "logo": "/api/public/branding/logos/org_…?v=…",
    "brandColor": "#D64545"
  },
  "schoolProfile": {
    "legalName": "Colegio Andes SpA",
    "schoolCode": "12345-5",
    "schoolType": "school",
    "educationLevels": ["basica", "media"],
    "commune": "Providencia",
    "countryCode": "CL",
    "privacyContactEmail": "privacidad@colegioandes.cl"
    // …
  }
}
```

Para poblar el padrón masivamente desde una planilla, sigue con [Importaciones](/api-organizacion/importaciones).
