---
title: "Transparencia"
description: "El portal de transparencia por API: secciones del Art. 14 ter, bloques derivados del RAT y versiones publicadas selladas con checksum."
url: "https://docs.edugoverna.com/api-organizacion/transparencia"
---
# Transparencia

El portal de transparencia es la página pública donde el colegio informa, letra por letra del Art. 14 ter, qué datos trata y para qué. Su contenido combina **secciones** redactadas por el colegio con **bloques de datos** que se derivan automáticamente del RAT, y cada publicación acuña una **versión sellada con checksum** — la prueba de qué decía el portal en qué momento. Con una API key el dominio es de lectura (`transparency.read`); la edición y la publicación son de consola. {{ className: 'lead' }}

El portal público de cada colegio vive en `https://app.edugoverna.com/transparencia/:slug`, y su contenido JSON se sirve sin autenticación en `GET /public/transparency/:slug` (con su PDF en `…/pdf`).

---

## Estado del portal {{ tag: 'GET', label: '/api/transparency/portal' }}

<Row>
  <Col>

    Requiere `transparency.read`. La foto completa del portal: configuración, secciones con su letra del artículo, advertencias de publicación (avisan, nunca bloquean), cobertura y el sello de la última versión publicada.

    ### Claves de la respuesta

    <Properties>
      <Property name="portal" type="object">
        `slug`, `enabled`, contactos y si enlaza los portales ARCO y de
        consentimiento (`arcoPortalLinked`, `consentPortalLinked`).
      </Property>
      <Property name="sections" type="array">
        Cada sección con `sectionKey`, `letter` (`a`–`l` o `null`), `title`,
        `enabled`, `position`, su cuerpo enriquecido y la guía del catálogo
        (`guidance`, `required`). El cuerpo de la sección con `letter: "a"` es el
        texto de la política de tratamiento vigente, no un texto aparte; vacío
        si aún no hay una aprobada.
      </Property>
      <Property name="policy" type="object | null">
        `{ title, versionNumber, effectiveFrom, publishedAt }` de la política
        vigente de la que sale la letra a); `null` si no hay ninguna.
      </Property>
      <Property name="warnings" type="array">
        `{ letter, sectionKey, message }` — lo que faltaría para cubrir el
        artículo.
      </Property>
      <Property name="coverage" type="object">
        `{ answered, total }`.
      </Property>
      <Property name="published" type="object | null">
        `{ versionId, versionNumber, publishedAt, checksum, catalogVersion }`
        de la última publicación; `null` si nunca se publicó.
      </Property>
      <Property name="stale" type="boolean">
        `true` cuando hay cambios sin publicar respecto de la última versión.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

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

    ```bash {{ title: 'cURL' }}
    curl "https://app.edugoverna.com/api/transparency/portal" \
      -H "x-api-key: {tu_api_key}"
    ```

    ```js
    const { portal, policy, sections, warnings, coverage, published, stale } =
      await fetch('https://app.edugoverna.com/api/transparency/portal', {
        headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY },
      }).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "policy": { "title": "Política de protección de datos", "versionNumber": 2 },
      "portal": {
        "slug": "colegio-andes",
        "enabled": true,
        "contactEmail": "privacidad@colegioandes.cl",
        "arcoPortalLinked": true,
        "consentPortalLinked": true
      },
      "sections": [
        {
          "sectionKey": "finalidades",
          "letter": "b",
          "title": "Para qué usamos los datos",
          "enabled": true,
          "position": 2,
          "required": true
        }
      ],
      "warnings": [],
      "coverage": { "answered": 12, "total": 12 },
      "published": {
        "versionId": "tv07…",
        "versionNumber": 7,
        "publishedAt": "2026-08-01T13:00:00.000Z",
        "checksum": "5d8c…",
        "catalogVersion": "eg-2026.08"
      },
      "stale": false
    }
    ```

  </Col>
</Row>

---

## Bloques de datos {{ tag: 'GET', label: '/api/transparency/blocks' }}

<Row>
  <Col>

    Requiere `transparency.read`. Los bloques son tablas que el portal materializa desde el RAT y la gobernanza — el colegio no las redacta, se derivan: `categorias`, `universo`, `finalidades`, `destinatarios`, `encargados`, `transferencias`, `retencion`, `medidas`, `decisiones_automatizadas`, `fuentes`.

    `GET /transparency/blocks` lista el resumen; `GET /transparency/blocks/:blockKind` resuelve uno con sus columnas y filas (un tipo desconocido responde `404 TRANSPARENCY_BLOCK_UNKNOWN`).

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/transparency/blocks/finalidades">

    ```bash {{ title: 'cURL' }}
    curl "https://app.edugoverna.com/api/transparency/blocks/finalidades" \
      -H "x-api-key: {tu_api_key}"
    ```

    ```js
    const { block } = await fetch(
      'https://app.edugoverna.com/api/transparency/blocks/finalidades',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "block": {
        "blockKind": "finalidades",
        "columns": ["Actividad", "Finalidad", "Base de licitud"],
        "rows": [
          ["Comunicaciones académicas", "Informar y coordinar…", "Consentimiento"]
        ]
      }
    }
    ```

  </Col>
</Row>

---

## Versiones publicadas {{ tag: 'GET', label: '/api/transparency/versions' }}

<Row>
  <Col>

    Requiere `transparency.read`. El historial sellado del portal, hasta 50 versiones en orden descendente. Cada versión guarda el **documento resuelto completo** (con los bloques ya materializados al momento de publicar), su `checksum` SHA-256 — el mismo que se imprime al pie del portal público — y el PDF archivado (`documentArtifactId`).

    `GET /transparency/versions/:versionId` agrega el campo `document` con la página completa tal como se publicó.

  </Col>
  <Col sticky>

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

    ```bash {{ title: 'cURL' }}
    curl "https://app.edugoverna.com/api/transparency/versions" \
      -H "x-api-key: {tu_api_key}"
    ```

    ```js
    const { versions } = await fetch(
      'https://app.edugoverna.com/api/transparency/versions',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "versions": [
        {
          "id": "tv07…",
          "versionNumber": 7,
          "checksum": "5d8c…",
          "catalogVersion": "eg-2026.08",
          "warnedLetters": [],
          "notes": "Actualiza destinatarios tras nuevo LMS",
          "publishedAt": "2026-08-01T13:00:00.000Z",
          "publishedByUserId": "usr_…",
          "documentArtifactId": "ar91…"
        }
      ]
    }
    ```

  </Col>
</Row>

---

## Política de privacidad y edición

`GET /transparency/policy` (con `transparency.read`) devuelve `{ policy, approvalPending }`: la política **vigente** —la última versión aprobada y publicada— o `null` si la propuesta aún espera su aprobación a cuatro ojos. La letra a) de la página pública muestra el texto de esa política; aprobarla no cambia la página ya publicada, aparece al publicar una nueva versión. `GET /transparency/preview` arma el documento completo con los datos de hoy, sin publicarlo.

La edición es de consola:

| Método y ruta                                                                                                                                             | Permiso                | Qué hace                                                                                                                                                                                                |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PUT /transparency/portal`                                                                                                                                | `transparency.write`   | Configura el portal. **Sólo sesión.**                                                                                                                                                                   |
| `POST /transparency/sections` · `PATCH /transparency/sections/:sectionId` · `PATCH /transparency/sections/reorder` · `DELETE` · `POST …/:sectionId/draft` | `transparency.write`   | Redacción de secciones; el cuerpo de la letra a) se rechaza con `409 TRANSPARENCY_SECTION_FROM_POLICY` (se edita en la política). **Sólo sesión.**                                                                                                                                                                |
| `PUT /transparency/policy`                                                                                                                                | `schools.write`        | Propone la política; queda `approvalPending` hasta que otra persona apruebe. **Sólo sesión.**                                                                                                           |
| `POST /transparency/publish`                                                                                                                              | `transparency.publish` | Acuña la versión sellada: responde `{ published: { versionId, versionNumber, checksum, publishedAt, warnedLetters, documentArtifactId } }`. **Sólo sesión** — publicar exige una persona identificable. |

---

## Marca del colegio {{ tag: 'PATCH', label: '/api/branding/color' }}

<Row>
  <Col>

    El color y el logo tiñen los portales públicos (transparencia, ARCO, consentimiento). Ambas rutas aceptan API key con `schools.write` y quedan auditadas.

    - `PATCH /branding/color` — cuerpo `{ "color": "#RRGGBB" }` (hex de 6 dígitos) o `{ "color": null }` para limpiar. Responde `{ ok, brandColor }`.
    - `POST /branding/logo` — `multipart/form-data`, campo `file`, PNG o JPEG. Responde `{ url, artifactId, contentType, sizeBytes }`; la `url` es pública y versionada por checksum.
    - `DELETE /branding/logo` — responde `{ ok, removed }`.

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="PATCH" label="/api/branding/color">

    ```bash {{ title: 'cURL' }}
    curl -X PATCH "https://app.edugoverna.com/api/branding/color" \
      -H "x-api-key: {tu_api_key}" \
      -H "Content-Type: application/json" \
      -d '{ "color": "#D64545" }'
    ```

    ```js
    const { ok, brandColor } = await fetch(
      'https://app.edugoverna.com/api/branding/color',
      {
        method: 'PATCH',
        headers: {
          'x-api-key': process.env.EDUGOVERNA_API_KEY,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ color: '#D64545' }),
      },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta' }}
    { "ok": true, "brandColor": "#D64545" }
    ```

  </Col>
</Row>

---

## Dominios propios {{ tag: 'GET', label: '/api/organization/portal-domains' }}

<Row>
  <Col>

    Sirve los portales públicos bajo el dominio del colegio (`transparencia.colegio.cl`, `denuncias.colegio.cl`). El portal debe existir antes de conectarle un dominio: un canal apagado responde `422 PORTAL_DOMAIN_SURFACE_NOT_READY`. Requiere el módulo `custom_domains` del plan; lectura con `schools.read`, escrituras con `schools.write`.

    Cada dominio indica su `surface` (`arco`, `transparency` o `convivencia`, el canal de denuncias), su `status` (`pending_dns` → `pending_certificate` → `active`; sólo `active` enruta) y los `dnsRecords` exactos que el colegio debe crear. La lectura trae también `surfaces`: los portales que existen (`surface`, `slug`, `canonicalPath`), que son los únicos a los que se les puede conectar un dominio. En despliegues sin dominios configurados la respuesta es inerte: `{ available: false, domains: [], cnameTarget: null }`.

    | Método y ruta | Qué hace |
    | --- | --- |
    | `POST /organization/portal-domains` | Registra `{ hostname, surface }`. |
    | `POST …/:portalDomainId/check` | Re-verifica DNS y certificado. Devuelve además `observations`: por cada registro, `expected`, `found` y `state` (`ok` · `missing` · `appended_zone` · `mismatch` · `unknown`). |
    | `PATCH …/:portalDomainId` | `{ redirectLegacyPath }`. |
    | `DELETE …/:portalDomainId` | Elimina el dominio. |

  </Col>
  <Col sticky>

    <CodeGroup title="Solicitud" tag="GET" label="/api/organization/portal-domains">

    ```bash {{ title: 'cURL' }}
    curl "https://app.edugoverna.com/api/organization/portal-domains" \
      -H "x-api-key: {tu_api_key}"
    ```

    ```js
    const { available, domains, cnameTarget } = await fetch(
      'https://app.edugoverna.com/api/organization/portal-domains',
      { headers: { 'x-api-key': process.env.EDUGOVERNA_API_KEY } },
    ).then((r) => r.json())
    ```

    </CodeGroup>

    ```json {{ title: 'Respuesta (recortada)' }}
    {
      "available": true,
      "cnameTarget": "portales.edugoverna.com",
      "domains": [
        {
          "id": "pd3f…",
          "hostname": "transparencia.colegioandes.cl",
          "surface": "transparency",
          "status": "active",
          "statusMessage": null,
          "redirectLegacyPath": true,
          "dnsRecords": [
            {
              "type": "CNAME",
              "name": "transparencia",
              "absoluteName": "transparencia.colegioandes.cl",
              "value": "portales.edugoverna.com",
              "purpose": "routing"
            }
          ],
          "verifiedAt": "2026-08-02T15:30:00.000Z"
        }
      ]
    }
    ```

  </Col>
</Row>
