---
title: "Autenticación"
description: "Cómo se autentica cada llamada a la API de Edugoverna: la credencial Bearer de la Partner API, las API keys de organización y las puertas de acceso."
url: "https://docs.edugoverna.com/autenticacion"
---
# Autenticación

Toda solicitud a la API debe autenticarse. Edugoverna distingue dos credenciales para integradores — la credencial **Bearer** de la Partner API y la **API key de organización** — y cada una funciona solo en su propia superficie: enviar una en las rutas de la otra produce un 401, nunca un acceso degradado. En esta guía verás cómo se emite cada credencial, qué verifica el servidor en cada llamada y cómo manejar los secretos de forma segura. {{ className: 'lead' }}

## Credencial de Partner API (Bearer)

La credencial de partner autentica a una plataforma externa que opera datos de uno o más colegios. En texto plano tiene la forma `keyId.secret` — un UUID como identificador y un secreto de 32 caracteres hexadecimales — y se envía completa en el encabezado `Authorization`:

```bash {{ title: 'Solicitud con credencial de partner' }}
curl "https://app.edugoverna.com/api/partner/v1/organizations/{organizationId}/directory/subjects" \
  -H "Authorization: Bearer {keyId}.{secret}"
```

El servidor no almacena el secreto: guarda el hash SHA-256 de la credencial completa y un prefijo (los primeros 12 caracteres) que la consola usa para identificarla. Por eso el valor en texto plano se muestra **una única vez**, al emitirla en **Integraciones → Partners**. Una credencial lleva además una etiqueta, un conjunto de permisos `partner.*`, una fecha de expiración opcional y una marca de sandbox.

### Las cuatro puertas, en orden

Autenticarse no basta: cada solicitud a una ruta de partner atraviesa cuatro verificaciones encadenadas, y la primera que falla determina la respuesta. Esto te permite diagnosticar un rechazo con precisión a partir del [código de error](/errores):

<Properties>
  <Property name="1. Credencial" type="401">
    La credencial existe, está activa (no revocada) y no ha expirado. Si no:
    `PARTNER_CREDENTIAL_INVALID` o `PARTNER_CREDENTIAL_EXPIRED`.
  </Property>
  <Property name="2. Conexión" type="403">
    Existe una conexión `organization_partner_connection` **activa** entre tu
    cuenta de partner y la organización de la URL. Si no:
    `PARTNER_CONNECTION_NOT_ACTIVE`. Aquí también se verifica que tu propia
    cuenta de partner esté activa en Edugoverna (`PARTNER_WORKSPACE_NOT_ACTIVE`
    si está suspendida o pendiente).
  </Property>
  <Property name="3. Contrato (Art. 15 bis)" type="403">
    La conexión tiene un DPA ejecutado y vigente. Si no:
    `PARTNER_DPA_NOT_ACTIVE`. Para conexiones de registro externo, el contrato
    exigido es la declaración jurada del registrador.
  </Property>
  <Property name="4. Permiso × alcance" type="403">
    La credencial tiene el permiso `partner.*` que la ruta exige
    (`PARTNER_PERMISSION_REQUIRED` si no), y la conexión otorga el alcance
    correspondiente, por ejemplo `subjects:read` (`PARTNER_SCOPE_REQUIRED` si
    no). Las condiciones del alcance — como limitar la lectura a titulares
    activos — moldean la respuesta.
  </Property>
</Properties>

Entre la puerta 3 y la 4 se descuenta además el [límite de tasa](/limites) de tu credencial: una ventana agotada responde `429 PARTNER_RATE_LIMITED` antes de evaluar permisos y alcances.

El colegio controla las puertas 2 a 4 desde su consola en cualquier momento: pausar la conexión, dejar vencer el DPA o retirar un alcance corta el acceso de inmediato, sin tocar tu credencial. Cada decisión — permitida o denegada — queda registrada en la auditoría del colegio con tu cuenta de partner como actor.

Los permisos asignables a una credencial de partner son: `partner.subjects.read`, `partner.subjects.write`, `partner.guardianships.write`, `partner.enrollments.write`, `partner.consents.read`, `partner.consents.write`, `partner.processing_activities.read`, `partner.processing_activities.write`, `partner.rights_requests.read`, `partner.rights_requests.write`, `partner.arco_portals.read`, `partner.arco_portals.write`, `partner.shared_subjects.read`, `partner.shared_campaigns.write`, `partner.registrar_invites.write` y `partner.share_requests.write`.

<Note>
  Las dos superficies de credenciales nunca aceptan el token de la otra. Si
  envías el encabezado `x-api-key` a una ruta `/partner/v1/...` — incluso junto
  a un Bearer válido — la solicitud se rechaza con `401 PARTNER_BEARER_REQUIRED`
  antes de evaluar nada más, y el intento queda auditado.
</Note>

## API key de organización

La API key de organización autentica los scripts e integraciones propias de un colegio. Se envía en el encabezado `x-api-key` y la key resuelve por sí sola la organización a la que pertenece — las rutas de organización no llevan el tenant en la URL:

```bash {{ title: 'Solicitud con API key de organización' }}
curl "https://app.edugoverna.com/api/data-subjects" \
  -H "x-api-key: {api_key}"
```

Las keys se crean en la consola, en **Integraciones → API keys**, con un permiso o más del catálogo asignable a keys — 29 permisos que cubren padrón (`subjects.read.*`, `subjects.write`, `students.*`, `guardians.*`), consentimientos (`consents.read`, `consents.write`), derechos (`rights_requests.*`), gobernanza (`processing_activities.*`, `dpia.read`, `transparency.read`), padrones compartidos (`registry_shares.*`, `shared_subjects.read`, `shared_campaigns.write`), integraciones (`integrations.*`), auditoría (`audit.read`) y más. Una key solo puede hacer lo que sus permisos nombran; todo lo demás responde 403.

Una solicitud sin el encabezado responde `401 API_KEY_REQUIRED`; una key inexistente, `401 INVALID_API_KEY`; una key revocada, `401 KEY_DISABLED`. Al igual que la credencial de partner, el valor completo se muestra una sola vez al crearla.

<div className="not-prose">
  <Button href="/gestion/credenciales" variant="text" arrow="right">
    <>Administra credenciales y API keys</>
  </Button>
</div>

## La conexión vista por el partner {{ tag: 'GET', label: '/v1/organizations/:organizationId/connection' }}

<Row>
  <Col>

    Lo que tu credencial puede saber de la conexión por la que acaba de entrar. No exige permiso ni alcance — todo lo que devuelve es sobre ti — pero sí pasa las tres primeras puertas: credencial, conexión activa y contrato.

    Dos respuestas que ninguna otra ruta daba: la dirección pública del portal ARCO **del colegio** (`organization.arcoPortal.url`, con dominio propio si lo tiene), que es donde debes derivar a un titular que ejerce derechos ante el responsable; y si los webhooks salen de esta organización (`webhooks.available`), que depende del módulo `integrations` de su plan.

    ### Campos de la respuesta

    <Properties>
      <Property name="connection" type="object">
        `id`, `status`, `connectionType`, `legalBasis`, `isSelfConnection`
        (tu propio workspace), `dpaContract` (o `null` en una autoconexión o
        un registro externo), `scopes` con sus condiciones y `permissions`.
      </Property>
      <Property name="organization" type="object">
        `id`, `name` y `arcoPortal` (`{ slug, url }`, o `null` si el colegio
        no tiene portal ARCO activo).
      </Property>
      <Property name="webhooks.available" type="boolean">
        `false` cuando el plan no incluye `integrations`: ningún endpoint
        recibirá entregas. Ver [Webhooks](/webhooks#registrar-un-endpoint).
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    <CodeGroup title="Request" tag="GET" label="/v1/.../connection">

    ```bash {{ title: 'cURL' }}
    curl https://app.edugoverna.com/api/partner/v1/organizations/org_2f7a/connection \
      -H "Authorization: Bearer {keyId}.{secret}"
    ```

    ```js
    const response = await fetch(
      'https://app.edugoverna.com/api/partner/v1/organizations/org_2f7a/connection',
      { headers: { Authorization: `Bearer ${keyId}.${secret}` } },
    )
    const connection = await response.json()
    ```

    </CodeGroup>

    ```json {{ title: 'Response' }}
    {
      "connection": {
        "id": "7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
        "status": "active",
        "connectionType": "api",
        "legalBasis": "consent",
        "isSelfConnection": false,
        "dpaContract": { "id": "dpa_01", "status": "active", "versionNumber": 1 },
        "scopes": [
          { "resourceNamespace": "subjects", "scopeType": "read", "conditions": { "onlyActive": true } },
          { "resourceNamespace": "consents", "scopeType": "write", "conditions": null }
        ],
        "permissions": ["partner.subjects.read", "partner.consents.write"]
      },
      "organization": {
        "id": "org_2f7a",
        "name": "Colegio San Martín",
        "arcoPortal": { "slug": "colegio-san-martin", "url": "https://arco.colegiosanmartin.cl/" }
      },
      "webhooks": { "available": true }
    }
    ```

  </Col>
</Row>

## Otros principales

Existen dos formas más de autenticación que verás mencionadas en estas páginas, pero que no están pensadas para integradores:

- **Sesión de miembro** — la cookie de sesión de la consola web. Varias rutas de organización aceptan sesión _o_ API key; algunas operaciones sensibles exigen sesión de miembro y responden `MEMBER_SESSION_REQUIRED` ante una key.
- **Tokens de portal público** — los enlaces firmados de un solo uso que reciben apoderados y titulares (portales de consentimiento y ARCO). Los emite y consume la propia plataforma.

## Recomendaciones de seguridad

- **Nunca incrustes la credencial en el código** ni la subas a un repositorio. Cárgala desde una variable de entorno o un gestor de secretos.
- **Usa una credencial por sistema.** Si dos servicios tuyos llaman a la API, pide credenciales separadas: el prefijo visible permite distinguirlas en la consola y revocar una sin afectar a la otra.
- **Rota periódicamente.** Emite la credencial nueva, despliega, y revoca la anterior. La revocación es inmediata.
- **Pide el mínimo.** Solicita solo los permisos y alcances que tu integración usa hoy; ampliar después es una operación de consola, no un cambio de código.
- **Ante una sospecha de filtración, revoca primero.** La credencial comprometida deja de servir en la solicitud siguiente, y la auditoría del colegio conserva qué hizo mientras estuvo activa.
