---
title: "Registradores y solicitudes"
description: "Endpoints de la Partner API para invitar registradores externos, pedir una conexión a un colegio por RBD y pedirle cursos a un colegio conectado."
url: "https://docs.edugoverna.com/solicitudes"
---
# Registradores y solicitudes

Todo lo que pides desde tu propio espacio de trabajo para llegar a un padrón: invitar a un **registrador externo** cuando el colegio no tiene cuenta, pedirle una **conexión** a un colegio que sí la tiene, y pedirle **cursos concretos** a un colegio ya conectado. En los tres casos tú pides y el dueño de los datos decide. Nunca creas ni eliges datos de un colegio. {{ className: 'lead' }}

<Note>
  En todas las rutas de esta página, `:organizationId` es **tu propia
  organización de espacio de trabajo**. Apuntarlas a otra organización
  responde `403 PARTNER_WORKSPACE_REQUIRED`. Te enteras de cada respuesta del
  dueño por webhook: `registrar.completed`, `connection.accepted`,
  `share.granted` y `share_request.decided` (ver [Webhooks](/webhooks)).
</Note>

---

## Invitar un registrador {{ tag: 'POST', label: '/v1/organizations/:organizationId/registrar-invites' }}

<Row>
  <Col>

    Crea una invitación para que un registrador externo cargue un padrón en el portal público y te lo comparta. El flujo del registrador está en la [guía](/guias/registrador-externo). Responde `201`.

    Permiso requerido: `partner.registrar_invites.write` · Alcance de conexión: `registrar_invites:write`.

    El **correo es obligatorio**: el portal le envía el código de un solo uso a esa dirección, y es la identidad de quien firma la declaración jurada. Con `deliver: "link"` no se envía correo y la respuesta trae `portalUrl` **una sola vez**, para que lo entregues por tu propio canal. El enlace sin el código de ese correo no sirve de nada, así que devolverlo no debilita la puerta. El token se guarda como hash: si pierdes el enlace, [reenviar la invitación](#reenviar-una-invitacion) (`POST …/registrar-invites/:inviteId/resend`) emite uno nuevo.

    Con `deliver: "email"` (el valor por defecto) `portalUrl` es `null`: el enlace sólo viaja en el correo.

    Una segunda invitación viva al mismo correo responde `409 REGISTRAR_INVITE_ALREADY_PENDING`.

    ### Atributos

    <Properties>
      <Property name="email" type="string">
        Correo del registrador (obligatorio).
      </Property>
      <Property name="invitedName" type="string">
        Nombre de quien recibe la invitación.
      </Property>
      <Property name="schoolNameHint" type="string">
        El colegio, para que el portal lo muestre prellenado.
      </Property>
      <Property name="message" type="string">
        Un mensaje para el registrador (se incluye en el correo).
      </Property>
      <Property name="externalRef" type="string">
        Tu correlativo (1–160). Vuelve en la lectura y en `registrar.completed`.
      </Property>
      <Property name="deliver" type="string">
        `email` (por defecto) o `link`.
      </Property>
      <Property name="processingActivityId" type="string">
        Una actividad de tratamiento de tu espacio de trabajo (uuid). Si la
        envías, el registrador podrá declarar qué estudiantes ya tienen el
        consentimiento firmado ante el colegio — ver [Consentimiento
        declarado](/guias/registrador-externo#consentimiento-declarado).
      </Property>
    </Properties>

    ### Errores de `processingActivityId`

    La actividad tiene que ser tuya, con base de licitud «consentimiento», con consentimiento exigido y con su versión vigente **publicada**. Las actividades de consentimiento **granular** no se aceptan: el formulario firmado no dice qué permisos se aceptaron.

    - `404 PROCESSING_ACTIVITY_NOT_FOUND`: no existe o no es de tu espacio de trabajo.
    - `409 REGISTRAR_ATTESTATION_ACTIVITY_NOT_CONSENT`: la base de licitud no es el consentimiento, o la actividad no lo exige.
    - `409 REGISTRAR_ATTESTATION_ACTIVITY_NOT_PUBLISHED`: la versión vigente no está publicada.
    - `409 REGISTRAR_ATTESTATION_GRANULAR_UNSUPPORTED`: la actividad es granular.

  </Col>
  <Col sticky>

    ```json {{ title: 'Response (deliver: "email")' }}
    {
      "inviteId": "5b1c9a2e-8f3d-4c7a-9e1b-2d4f6a8c0e13",
      "status": "pending",
      "externalRef": "colegio-rbd-12345",
      "schoolNameHint": "Liceo Costa Azul",
      "processingActivityId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "registryOrganizationId": null,
      "expiresAt": "2026-10-09T12:00:00.000Z",
      "completedAt": null,
      "createdAt": "2026-09-25T12:00:00.000Z",
      "portalUrl": null
    }
    ```

    Con `deliver: "link"` la misma respuesta trae `portalUrl` con el enlace, por única vez:

    ```json {{ title: 'portalUrl (deliver: "link")' }}
    { "portalUrl": "https://app.edugoverna.com/registrar/portal/…" }
    ```

  </Col>
</Row>

---

## Leer invitaciones {{ tag: 'GET', label: '/v1/organizations/:organizationId/registrar-invites/:inviteId' }}

<Row>
  <Col>

    El estado de una invitación: `pending`, `accepted`, `expired` o `revoked`. Cuando el registrador cargó su primer curso, `registryOrganizationId` es la organización del registro y `completedAt` la fecha. `GET …/registrar-invites` lista todas; con `?externalRef=` filtra por tu correlativo. Cada invitación trae `processingActivityId` (`null` si no pediste consentimiento declarado). Estas lecturas no devuelven `portalUrl`.

    Permiso requerido: `partner.registrar_invites.write` · Alcance de conexión: `registrar_invites:write`.

  </Col>
</Row>

---

## Reenviar una invitación {{ tag: 'POST', label: '/v1/organizations/:organizationId/registrar-invites/:inviteId/resend' }}

<Row>
  <Col>

    Emite un enlace nuevo para una invitación existente y extiende su plazo 14 días. El enlace anterior **deja de abrir** de inmediato. Es la salida cuando el registrador perdió el correo, o cuando creaste la invitación con `deliver: "link"` y perdiste el enlace: el token se guarda como hash y no se puede recuperar. Una invitación revocada no se reenvía (`409 REGISTRAR_INVITE_REVOKED`). Si el registrador ya cargó su padrón, el reenvío le devuelve el acceso a su panel.

    Permiso requerido: `partner.registrar_invites.write` · Alcance de conexión: `registrar_invites:write`. El cuerpo es opcional.

    Con `deliver: "email"` (el valor por defecto) se envía el correo y `portalUrl` es `null`. Con `deliver: "link"` **no se envía correo** y la respuesta trae el `portalUrl` nuevo **una sola vez**, para que lo entregues por tu canal; el portal sigue pidiendo el código enviado al correo invitado. La respuesta es la invitación (la misma forma de la lectura) más `portalUrl`.

    Errores:

    - `404 REGISTRAR_INVITE_NOT_FOUND`: no existe o no es de tu espacio de trabajo.
    - `409 REGISTRAR_INVITE_REVOKED`: la invitación fue cancelada.
    - `429 REGISTRAR_INVITE_RESEND_RATE_LIMITED`: más de 5 reenvíos de la misma invitación en 5 minutos (`retryAfterSeconds` en el detalle). Cada reenvío rota el token, así que el límite acota cuántos enlaces vivos puede haber.

    ### Atributos

    <Properties>
      <Property name="deliver" type="string">
        `email` (por defecto) o `link`.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    ```bash {{ title: 'cURL' }}
    curl -X POST https://app.edugoverna.com/api/partner/v1/organizations/org_milectura/registrar-invites/5b1c9a2e-8f3d-4c7a-9e1b-2d4f6a8c0e13/resend \
      -H "Authorization: Bearer {keyId}.{secret}" \
      -H "Content-Type: application/json" \
      -d '{ "deliver": "link" }'
    ```

    ```json {{ title: 'Response (deliver: "link")' }}
    {
      "inviteId": "5b1c9a2e-8f3d-4c7a-9e1b-2d4f6a8c0e13",
      "status": "pending",
      "externalRef": "colegio-rbd-12345",
      "schoolNameHint": "Liceo Costa Azul",
      "processingActivityId": null,
      "registryOrganizationId": null,
      "expiresAt": "2026-10-29T12:00:00.000Z",
      "completedAt": null,
      "createdAt": "2026-09-25T12:00:00.000Z",
      "portalUrl": "https://app.edugoverna.com/registrar/portal/…"
    }
    ```

  </Col>
</Row>

---

## Cancelar una invitación {{ tag: 'DELETE', label: '/v1/organizations/:organizationId/registrar-invites/:inviteId' }}

<Row>
  <Col>

    Revoca una invitación **sin padrón**: su enlace deja de abrir. Responde `200` con el id y el estado.

    Permiso requerido: `partner.registrar_invites.write` · Alcance de conexión: `registrar_invites:write`.

    Una invitación cuyo registrador ya cargó un padrón **no se puede cancelar**: responde `409 REGISTRAR_INVITE_ALREADY_ACCEPTED`. El enlace es la única puerta del colegio a su propio panel, así que cortar el acceso a un padrón es del registrador (lo hace desde el portal, en «Gestionar acceso»), no de quien lo recibe. Si no existe o no es de tu espacio de trabajo, `404 REGISTRAR_INVITE_NOT_FOUND`.

  </Col>
  <Col sticky>

    ```bash {{ title: 'cURL' }}
    curl -X DELETE https://app.edugoverna.com/api/partner/v1/organizations/org_milectura/registrar-invites/5b1c9a2e-8f3d-4c7a-9e1b-2d4f6a8c0e13 \
      -H "Authorization: Bearer {keyId}.{secret}"
    ```

    ```json {{ title: 'Response' }}
    {
      "inviteId": "5b1c9a2e-8f3d-4c7a-9e1b-2d4f6a8c0e13",
      "status": "revoked"
    }
    ```

  </Col>
</Row>

---

## Pedir una conexión por RBD {{ tag: 'POST', label: '/v1/organizations/:organizationId/connection-requests' }}

<Row>
  <Col>

    Le pide una conexión al colegio identificado por su **RBD**. Si hay un colegio con cuenta y ese RBD, recibe la solicitud en su consola y decide. Responde `202` con la **misma forma** haya o no un colegio detrás: no hay un buscador de RBD, porque diría qué colegios son clientes. Te enteras del `organizationId` cuando el colegio acepta, por `connection.accepted` o leyendo la solicitud.

    Permiso requerido: `partner.share_requests.write` · Alcance de conexión: `share_requests:write`.

    Un RBD mal formado responde `422 RBD_INVALID`. Una solicitud que no llega a ningún colegio queda `pending` y pasa a `expired` a los 30 días.

    ### Atributos

    <Properties>
      <Property name="rbd" type="string">
        El RBD, con o sin dígito verificador (`12345` o `12345-5`).
      </Property>
      <Property name="message" type="string">
        Un mensaje para el colegio.
      </Property>
      <Property name="requestedScopes" type="array">
        Los alcances que pides, como en una solicitud desde la consola.
      </Property>
      <Property name="externalRef" type="string">
        Tu correlativo; vuelve al leer la solicitud.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    ```json {{ title: 'Response' }}
    { "connectionRequestId": "0f3e…", "status": "pending" }
    ```

    ```json {{ title: 'GET …/connection-requests/:connectionRequestId' }}
    {
      "connectionRequestId": "0f3e…",
      "status": "accepted",
      "rbd": "12345",
      "externalRef": null,
      "organizationId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
      "organizationPartnerConnectionId": "8a1b…",
      "createdAt": "2026-09-25T12:00:00.000Z",
      "decidedAt": "2026-09-26T09:30:00.000Z"
    }
    ```

  </Col>
</Row>

---

## Pedir cursos a un colegio {{ tag: 'POST', label: '/v1/organizations/:organizationId/share-requests' }}

<Row>
  <Col>

    Le pide a un colegio **ya conectado** que te comparta ciertos cursos. Nombras los grupos como los conoces (`"4°M A"`, año 2027). El colegio los ve en su consola y aprueba eligiendo **sus grupos reales**: tu nombre es una referencia, no una llave. Cada grupo aprobado se vuelve un registro compartido normal (`share.granted`), y la decisión llega en `share_request.decided` con `shareIds`. Responde `201`.

    Permiso requerido: `partner.share_requests.write` · Alcance de conexión: `share_requests:write`.

    Sin una conexión activa con esa organización, `404 PARTNER_CONNECTION_NOT_FOUND`. `GET …/share-requests/:shareRequestId` devuelve el estado (`pending`, `approved` o `declined`), los `shareIds` y la nota del colegio.

    ### Atributos

    <Properties>
      <Property name="organizationId" type="string">
        El colegio (el que recibiste en `connection.accepted`).
      </Property>
      <Property name="groups" type="array">
        Hasta 50 `{ kind, name, schoolYear? }`, con `kind` `course`,
        `segment` o `cohort`.
      </Property>
      <Property name="message" type="string">
        Para qué los necesitas.
      </Property>
      <Property name="externalRef" type="string">
        Tu correlativo.
      </Property>
    </Properties>

  </Col>
  <Col sticky>

    ```json {{ title: 'Response' }}
    {
      "shareRequestId": "a4c2…",
      "organizationId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
      "partnerAccountId": "…",
      "status": "pending",
      "groups": [{ "kind": "course", "name": "4°M A", "schoolYear": 2027 }],
      "message": "Test vocacional 2027",
      "externalRef": null,
      "shareIds": [],
      "decidedAt": null,
      "decisionNote": null,
      "createdAt": "2026-09-25T12:00:00.000Z"
    }
    ```

  </Col>
</Row>
