Webhooks

Con los webhooks, tu aplicación se entera en el momento en que algo ocurre en Edugoverna — se registra una decisión de consentimiento, entra una solicitud ARCO por el portal, falla el envío de un mensaje — sin necesidad de consultar la API en un ciclo de polling. En esta guía verás cómo se firman las entregas, qué forma tiene el sobre de cada evento y el catálogo completo de eventos disponibles.

Registrar un endpoint

Los endpoints de webhook se administran desde la Consola (Integraciones → Webhooks) o mediante la API de gestión de webhooks. Al crear un endpoint eliges un nombre, la URL de destino (solo https://), los eventos a los que te suscribes y, opcionalmente, un secreto propio. Si omites el secreto, el servidor genera uno con prefijo whsec_ y lo devuelve una única vez en la respuesta de creación.

Los webhooks requieren que la organización tenga habilitado el módulo integrations en su licencia. Sin ese módulo, crear un endpoint o activarlo responde 409 INTEGRATIONS_MODULE_REQUIRED, y la Consola lo avisa en Integraciones → Webhooks. Si el módulo se retira después de registrar endpoints, los eventos no se entregan y cada omisión queda en la auditoría de la organización (outbound_webhook_event · skipped_module_disabled). Un partner puede comprobar de antemano si los webhooks salen de una organización con webhooks.available en la conexión.


Cabeceras de cada entrega

Cada entrega es un POST con content-type: application/json a la URL del endpoint, acompañado de tres cabeceras:

  • Nombre
    x-edugoverna-signature
    Tipo
    string
    Descripción

    Firma HMAC-SHA-256 en hexadecimal, calculada como HMAC(secret, "{timestamp}.{rawBody}"). El nombre de esta cabecera es configurable por endpoint (signatureHeader); el valor por defecto es x-edugoverna-signature.

  • Nombre
    x-edugoverna-event
    Tipo
    string
    Descripción

    El tipo de evento entregado, por ejemplo consent.decision.recorded.

  • Nombre
    x-edugoverna-timestamp
    Tipo
    string
    Descripción

    El instante en que se firmó la solicitud, en segundos Unix. Úsalo para reconstruir la cadena firmada y para rechazar entregas antiguas.


Verificación de la firma

Para comprobar que una entrega proviene efectivamente de Edugoverna:

  1. Lee la cabecera x-edugoverna-timestamp y el cuerpo crudo de la solicitud (los bytes exactos recibidos — no re-serialices el JSON ya parseado).
  2. Construye la cadena firmada como ${timestamp}.${rawBody}, con un punto literal entre el timestamp y el cuerpo.
  3. Calcula HMAC-SHA-256(secret, cadenaFirmada) en hexadecimal y compáralo con la cabecera de firma usando una comparación de tiempo constante.
  4. Rechaza la entrega si |ahoraEnSegundos - timestamp| > 300 (5 minutos), para protegerte de la repetición de una solicitud capturada.
Verificar una entrega
import crypto from "node:crypto"

const TOLERANCE_SECONDS = 300 // 5 minutos

function verifyWebhook({ rawBody, headers, secret }) {
  const timestamp = headers["x-edugoverna-timestamp"]
  const signature = headers["x-edugoverna-signature"]

  if (!timestamp || !signature) {
    return false
  }

  // Rechaza entregas demasiado antiguas (protección anti-replay).
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp))
  if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) {
    return false
  }

  // Recalcula la firma sobre `${timestamp}.${rawBody}`.
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex")

  // Comparación de tiempo constante.
  const a = Buffer.from(expected, "hex")
  const b = Buffer.from(signature, "hex")
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

El mismo esquema para los conectores ARCO

Si además recibes acciones de derechos como conector externo (external_post, configurado por el colegio en su portal ARCO), la firma es idéntica: cada despacho llega con x-edugoverna-timestamp y la cabecera de firma que el colegio configuró para el conector, calculada como HMAC(secret, "{timestamp}.{rawBody}") con el secreto del conector. El cuerpo trae en callback la URL a la que respondes y el esquema (signatureScheme, timestampHeader, toleranceSeconds). Tu callback se firma igual, en sentido inverso: envía x-edugoverna-timestamp y x-edugoverna-signature sobre "{timestamp}.{rawBody}"; sin timestamp responde 401 ARCO_ACTION_CALLBACK_TIMESTAMP_REQUIRED, fuera de la ventana de 300 s 401 ARCO_ACTION_CALLBACK_STALE, y con firma incorrecta 401 ARCO_ACTION_CALLBACK_INVALID_SIGNATURE. Un solo verificador sirve para ambos.

Guarda el secreto del webhook con el mismo cuidado que una credencial de API: quien lo conozca puede fabricar entregas válidas. Si sospechas que se filtró, rota el secreto — la rotación genera uno nuevo y lo devuelve una sola vez.


El sobre del evento

Toda entrega comparte el mismo sobre. Los campos específicos del evento viajan dentro de data.

  • Nombre
    event
    Tipo
    string
    Descripción

    El tipo de evento, uno de los del catálogo. Coincide con la cabecera x-edugoverna-event.

  • Nombre
    sourceType
    Tipo
    string
    Descripción

    La clase de recurso que originó el evento: consent_request, consent_delivery, rights_request, security_incident o incident_notification.

  • Nombre
    sourceId
    Tipo
    string
    Descripción

    El identificador del recurso de origen. Úsalo para consultar el estado actual por la API si lo necesitas.

  • Nombre
    occurredAt
    Tipo
    string
    Descripción

    Fecha y hora ISO 8601 en que se construyó la entrega.

  • Nombre
    context
    Tipo
    object
    Descripción

    Solo en algunos eventos. Hechos del evento que no viven en el recurso de origen: en consent.recaptured, reason y supersededConsentRequestId; en consent.invalidated, invalidationReason; en rights_request.sla_*, stage y effectiveDueAt. Escalares planos, nunca datos personales.

  • Nombre
    data
    Tipo
    object | null
    Descripción

    La proyección del recurso de origen en el momento de la entrega. Su forma depende de sourceType — ver los payloads por sourceType.

Sobre de ejemplo
{
  "event": "consent.decision.recorded",
  "sourceType": "consent_request",
  "sourceId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
  "occurredAt": "2026-08-25T14:03:22.511Z",
  "data": {
    "id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
    "status": "granted"
    // ... proyección completa según sourceType
  }
}

Catálogo de eventos

Estos son los 29 tipos de evento del catálogo, agrupados igual que en el selector de la Consola. La suscripción es por lista explícita o mediante el comodín ["*"].

Consentimiento

Eventos sobre el ciclo de vida de una solicitud de consentimiento. sourceType es consent_request. Ver Consentimientos.

  • Nombre
    consent.requested
    Descripción

    Se creó una solicitud de consentimiento (desde la Consola, una campaña o la API) y sus entregas quedaron encoladas.

  • Nombre
    consent.decision.recorded
    Descripción

    Se registró una decisión (otorgada o denegada), también cuando el titular acota o vuelve a otorgar desde el portal de transparencia. En campañas sobre padrones compartidos, este y todos los eventos de consentimiento se emiten para cada parte — ver la proyección para partners.

  • Nombre
    consent.revoked
    Descripción

    El titular o un operador revocó un consentimiento otorgado. context.revocationSource dice por dónde: portal (el enlace del consentimiento), transparency_self_service (el portal de transparencia), staff (la Consola o la API de organización) o partner (la Partner API) o registrar (el registrador externo transmitió un retiro que la familia comunicó al colegio; ver Consentimiento declarado). Una misma solicitud puede revocarse más de una vez si el titular vuelve a otorgar entre medio: cada retiro llega como su propio evento.

  • Nombre
    consent.expired
    Descripción

    Una solicitud pendiente pasó su expiresAt sin decisión y fue marcada como expirada por el barrido programado.

  • Nombre
    consent.invalidated
    Descripción

    Una solicitud fue anulada: data.status es invalidated y context.invalidationReason dice por qué. "campaign_closed": se cerró su campaña antes de la decisión. "superseded_by_campaign": una campaña nueva reemplazó la solicitud pendiente. "version_superseded": se publicó una versión de la actividad que exige volver a consentir, y la anulada puede haber estado otorgada. "superseded_by_titular": el titular, ya con edad para decidir, respondió una solicitud nueva y su decisión reemplazó el «sí» que había dado el apoderado. "registration_rejected": el colegio rechazó un registro público ya otorgado. Las solicitudes anuladas no pasan después a expired, así que este es su evento terminal.

  • Nombre
    consent.recaptured
    Descripción

    La plataforma anuló una solicitud y emitió una nueva sin que nadie la pidiera: una pendiente se redirigió a quien decide hoy porque el titular cruzó la edad en que eso cambia (context.reason = "age_threshold", según la ageTransitionPolicy de la versión; ocurre en el próximo recordatorio o reenvío, o al intentar firmarla), o se publicó una versión del aviso que invalida las anteriores ("notice_version"). data es la solicitud nueva; context.supersededConsentRequestId es la anterior, cuando la hay. No se emite consent.requested por estas solicitudes.

  • Nombre
    consent.student_notified
    Descripción

    En una campaña compartida con aviso al estudiante activo, EduGoverna procesó el aviso al estudiante después de que su apoderado otorgó. context.outcome es sent (se envió a su correo registrado) o no_contact (el estudiante no tiene correo; no se envió nada).

consent.decision.recorded
{
  "event": "consent.decision.recorded",
  "sourceType": "consent_request",
  "sourceId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
  "occurredAt": "2026-08-25T14:03:22.511Z",
  "data": {
    "id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
    "status": "granted",
    "decisionActorType": "guardian",
    "requestedAt": "2026-08-20T12:00:00.000Z",
    "expiresAt": "2026-09-20T12:00:00.000Z",
    "deliveries": [
      {
        "channel": "email",
        "status": "opened",
        "destinationMasked": "m…a@example.cl"
      }
    ],
    "latestDecision": {
      "decision": "granted",
      "decidedAt": "2026-08-25T14:03:21.000Z",
      "captureMethod": "portal_link_otp"
    }
    // ... campos adicionales de la proyección
  }
}

Envíos de consentimiento

Eventos sobre los mensajes individuales (email, WhatsApp) con que se solicita el consentimiento.

  • Nombre
    consent.delivery.updated
    Descripción

    Una entrega cambió de estado (queued, sent, delivered, opened, failed, …), ya sea por confirmación del proveedor de mensajería o por una actualización manual. sourceType es consent_delivery y la entrega usa el nuevo estado como occurrenceKey, así que cada transición llega una vez.

  • Nombre
    consent.delivery.opened
    Descripción

    El titular o apoderado abrió por primera vez el enlace del portal de consentimiento. sourceType es consent_request: el payload es la solicitud completa, no la fila de la entrega.

  • Nombre
    envelopes.job.completed
    Descripción

    Terminó un despacho de sobres por lote. sourceType es consent_envelope_job y data trae el estado y los conteos; los resultados por fila se leen en el job.

  • Nombre
    consent.delivery.failed
    Descripción

    El proveedor reportó una falla definitiva para esa entrega. En WhatsApp, failureReason también puede indicar que el canal dejó de estar disponible (cuenta restringida, sin fondeo, mensaje no aprobado o tope de gasto alcanzado): son fallas permanentes que no se reintentan hasta que la organización las resuelva y use «Reenviar». sourceType es consent_delivery; se emite además del consent.delivery.updated correspondiente.

consent.delivery.failed
{
  "event": "consent.delivery.failed",
  "sourceType": "consent_delivery",
  "sourceId": "7c1f00aa-2e43-4b7e-9f14-3d2e9a7c51d0",
  "occurredAt": "2026-08-25T09:12:44.008Z",
  "data": {
    "id": "7c1f00aa-2e43-4b7e-9f14-3d2e9a7c51d0",
    "consentRequestId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
    "channel": "whatsapp",
    "destinationMasked": "+569••••1234",
    "status": "failed",
    "failedAt": "2026-08-25T09:12:40.000Z",
    "failureReason": "Provider delivery failed."
    // ... campos adicionales de la fila de entrega
  }
}

Padrones compartidos

Eventos de la relación con los colegios y registradores que te comparten datos. Llegan sólo a tu espacio de trabajo y su data es una proyección propia ("projection": "partner_lifecycle"): ids, estados, fechas y nombres de organizaciones y grupos, nunca personas. Ver Registradores y solicitudes.

  • Nombre
    share.granted
    Descripción

    Un colegio o registrador te compartió un grupo. sourceType es registry_share y data trae el grupo (groupName, groupKind, course), el dueño (owner) y memberCount.

  • Nombre
    share.revoked
    Descripción

    El dueño dejó de compartirte el grupo, directamente o al revocar la conexión. context.reason dice por qué cuando se sabe. Los consentimientos ya otorgados siguen vigentes (son evidencia). Lo que pierdes es el padrón.

  • Nombre
    share.members.changed
    Descripción

    La composición de un grupo cambió en una carga del registrador. context.addedCount y context.removedCount traen los conteos; relee el grupo con since para saber quiénes.

  • Nombre
    share_request.decided
    Descripción

    El colegio aprobó o rechazó tu solicitud de cursos. sourceType es share_request; data.shareIds son los shares creados y data.externalRef, el que mandaste al pedirlos.

  • Nombre
    registrar.completed
    Descripción

    El registrador que invitaste cargó su primer curso: la invitación se cumplió. sourceType es registrar_invite. data es la proyección partner_lifecycle de la invitación: inviteId, status, externalRef (tu correlativo), registryOrganizationId (el registro creado), schoolName y schoolRbd (el colegio que declaró el registrador; schoolName cae al schoolNameHint de la invitación si aún no hay registro), acceptedAt, processingActivityId (la actividad de la invitación, null si no llevaba consentimiento declarado) y shareIds (los grupos que ya te comparte).

Conexiones

  • Nombre
    connection.accepted
    Descripción

    Un colegio aceptó la conexión (tu solicitud, incluida la hecha por RBD). sourceType es partner_connection; data.organization trae su organizationId. Si la conexión nació de una solicitud tuya, data.connectionRequestId y data.externalRef la identifican; ambos son null cuando fue el colegio quien te invitó.

  • Nombre
    connection.revoked
    Descripción

    El colegio revocó la conexión. También recibes un share.revoked por cada grupo que estaba compartido.

  • Nombre
    dpa.activated
    Descripción

    Entró en vigor un contrato de encargo (Art. 15 bis) con el colegio, también al aceptarse una conexión. sourceType es dpa_contract.

  • Nombre
    dpa.expired
    Descripción

    Venció el contrato (effectiveTo pasó). Desde ese momento la API responde 403 PARTNER_DPA_NOT_ACTIVE sobre ese colegio.

Derechos ARCO

Eventos sobre solicitudes de derechos. sourceType es rights_request y data trae la proyección completa de la solicitud tal como la ve la Consola. Ver Derechos ARCO.

  • Nombre
    rights_request.created
    Descripción

    Entró una solicitud nueva, por ejemplo a través del portal ARCO público.

  • Nombre
    rights_request.updated
    Descripción

    La solicitud recibió actividad nueva, como un mensaje de seguimiento del titular por el portal.

  • Nombre
    rights_request.document.added
    Descripción

    El titular adjuntó uno o más documentos a la solicitud (al crearla o en un seguimiento posterior).

  • Nombre
    rights_request.export_ready
    Descripción

    El paquete de exportación aprobado terminó de construirse y quedó disponible para descarga. Se entrega con sourceType: "rights_request" y un occurrenceKey único por construcción del paquete.

  • Nombre
    rights_request.closed
    Descripción

    La solicitud fue cerrada (respondida o denegada) con su certificado de cierre.

rights_request.created
{
  "event": "rights_request.created",
  "sourceType": "rights_request",
  "sourceId": "9e2a41d7-6c3b-4f0e-8a17-b5d4c2e8f901",
  "occurredAt": "2026-08-25T16:40:02.113Z",
  "data": {
    "id": "9e2a41d7-6c3b-4f0e-8a17-b5d4c2e8f901",
    "requestType": "access",
    "status": "received"
    // ... proyección completa de la solicitud
  }
}

Plazos ARCO

Eventos sobre el plazo legal de respuesta de una solicitud de derechos. Se emiten en el mismo barrido que envía el recordatorio por correo al equipo responsable; sourceType es rights_request y context trae stage y effectiveDueAt (la fecha vigente, con prórroga si la hubo).

  • Nombre
    rights_request.sla_due_soon
    Descripción

    Una solicitud abierta entró en la ventana previa a su vencimiento.

  • Nombre
    rights_request.sla_overdue
    Descripción

    Una solicitud abierta superó su fecha de vencimiento sin cierre. Se repite cada tres días hábiles mientras siga abierta, cada vez con su propio occurrenceKey (overdue, overdue_repeat:1, overdue_repeat:2…) y con context.repeatIndex y context.escalated en las repeticiones.

Incidentes

Eventos sobre incidentes de seguridad y sus notificaciones a titulares.

  • Nombre
    security.incident.created
    Descripción

    Se registró un incidente de seguridad y se encolaron las notificaciones a los titulares afectados. sourceType es security_incident.

  • Nombre
    security.incident.notification.updated
    Descripción

    La notificación de incidente a un titular cambió de estado según el proveedor de mensajería. sourceType es incident_notification y el nuevo estado viaja como occurrenceKey.

security.incident.created
{
  "event": "security.incident.created",
  "sourceType": "security_incident",
  "sourceId": "4f8b2c6d-1a0e-4d3f-9c7b-2e5a8d1f6b09",
  "occurredAt": "2026-08-25T18:05:37.902Z",
  "data": {
    "id": "4f8b2c6d-1a0e-4d3f-9c7b-2e5a8d1f6b09"
    // ... fila del incidente
  }
}

Payloads por sourceType

El contenido de data es la proyección del recurso de origen en el momento en que se construye la entrega (no en el momento del hecho: si el recurso cambió entre medio, verás su estado más reciente).

  • Nombre
    consent_request
    Tipo
    object
    Descripción

    La proyección completa de la solicitud de consentimiento: la fila principal (id, status, decisionActorType, processingActivityVersionId, targetSubjectId, requestedAt, expiresAt, …) más deliveries[], latestDecision (con su desglose de opciones y evidencia) y latestEvent. Si la solicitud pertenece a una campaña se agregan campaignId, campaignMode, campaignPublicSubjectFlow, campaignIdentityVerificationMode, campaignStatus, campaignContactEntryId y campaignContactEntry.

  • Nombre
    consent_delivery
    Tipo
    object
    Descripción

    La fila de la entrega: id, consentRequestId, channel, destinationMasked, status, deliveryProvider, providerMessageId, las marcas de tiempo queuedAt / sentAt / deliveredAt / openedAt / failedAt y failureReason.

  • Nombre
    rights_request
    Tipo
    object
    Descripción

    La proyección completa de la solicitud de derechos, incluida su actividad asociada (documentos, mensajes, cierre), tal como la entrega la API de la Consola.

  • Nombre
    security_incident
    Tipo
    object
    Descripción

    La fila del incidente de seguridad.

  • Nombre
    incident_notification
    Tipo
    object
    Descripción

    La fila de la notificación de incidente a un titular, con su estado de entrega.


Proyección para partners (shared_partner)

En una campaña sobre un padrón compartido, la campaña vive en la organización del partner mientras la solicitud de consentimiento vive en la del sostenedor de los datos. Todo evento de consentimiento de esa solicitud (consent.requested, consent.decision.recorded, consent.revoked, consent.expired, consent.invalidated, consent.recaptured y los tres de envío) se emite para ambas partes con el mismo occurrenceKey:

  • La copia del sostenedor lleva la proyección completa descrita arriba.
  • La copia que reciben los endpoints de la organización del partner lleva una proyección reducida, construida como lista blanca campo a campo. Se identifica a sí misma con "projection": "shared_partner" para que nunca se confunda con una proyección completa a la que "le faltan" campos.

En la copia del partner, sourceType es siempre consent_request, también en los eventos de envío (el partner no tiene la fila de la entrega). Esos eventos agregan en context el id de la entrega (consentDeliveryId, el mismo de portalAccessLinks), deliveryChannel, deliveryStatus y, si falló, failureCode (destination_missing, dispatch_blocked o provider_failed), nunca el texto del proveedor ni el destino.

En una campaña segmentada por RUT el partner no recibe nada de una solicitud hasta que haya una decisión: un consent.requested le diría qué RUT están en el padrón, que es justo lo que ese modo protege.

La proyección reducida se limita a lo que el partner ya puede leer por su propio endpoint de sujetos compartidos: no incluye RUT (ni enmascarado), contactos en ninguna forma, el nombre del titular, el id interno del sujeto en la organización dueña, ni el contenido de la evidencia o texto libre de operadores. Sí trae el id y el checksum del manifiesto sellado (latestDecision.evidence), que no nombran a nadie y se validan en /api/public/evidence/verify. latestDecision.decisionAuthority indica el rol que decidió (titular, apoderado), nunca quién.

latestDecision.captureMethod dice cómo se capturó la decisión: portal_link_otp (la familia, con el enlace y el código de un solo uso) o registrar_attestation, el consentimiento declarado por el establecimiento mediante un registrador externo. Esta segunda no es una firma con código: su evidencia es una declaración jurada más la custodia del colegio, y tu integración debería mostrarla distinta. Los retiros que el registrador transmite llegan como consent.revoked con context.revocationSource: "registrar".

Payload shared_partner
{
  "event": "consent.decision.recorded",
  "sourceType": "consent_request",
  "sourceId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
  "occurredAt": "2026-08-25T14:03:22.511Z",
  "data": {
    "projection": "shared_partner",
    "consentRequestId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
    "dataOwnerOrganizationId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
    "registryShareId": "a3bb189e-8bf9-3888-9912-ace4e6543002",
    "campaignId": "16fd2706-8baf-433b-82eb-8c7fada847da",
    "campaignStatus": "active",
    "campaignMode": "per_subject",
    "campaignOrigin": "operator",
    "campaignContactEntryId": "886313e1-3b8a-5372-9b90-0c9aee199e5d",
    "partnerExternalId": "par_04421",
    "processingActivityVersionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "subject": {
      "referenceCode": "EST-04421",
      "subjectType": "student",
      "requestedForAgeBand": "under_14"
    },
    "status": "granted",
    "decisionActorType": "guardian",
    "requestedAt": "2026-08-20T12:00:00.000Z",
    "expiresAt": "2026-09-20T12:00:00.000Z",
    "updatedAt": "2026-08-25T14:03:21.000Z",
    "latestDecision": {
      "id": "0b8e4c2a-5d1f-4a6b-8c3e-7f9d2a1b6c4e",
      "decision": "granted",
      "decisionAuthority": "guardian",
      "captureMethod": "portal_link_otp",
      "decidedAt": "2026-08-25T14:03:21.000Z",
      "effectiveFrom": "2026-08-25T14:03:21.000Z",
      "effectiveTo": null,
      "revokedAt": null,
      "evidence": {
        "id": "…:manifest",
        "checksum": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b"
      }
    }
  }
}

campaignOrigin dice quién originó la campaña: operator (la lanzaste tú) o registrar_attestation (los consentimientos que un registrador externo declaró firmados; no se enviaron mensajes). Es null si la solicitud no pertenece a una campaña. campaignContactEntryId es el id del sobre y partnerExternalId tu correlativo, tal como lo mandaste al despachar. Cualquier otro sourceType en una copia de audiencia partner no tiene proyección aprobada, y por diseño no se divulga nada: data llega null.


Suscripción comodín

El valor almacenado ["*"] suscribe el endpoint a todos los eventos, incluidos los que se agreguen al catálogo en el futuro. El comodín debe ir solo: enabledEvents acepta ["*"] o una lista de tipos explícitos, nunca una mezcla. Si omites enabledEvents al crear el endpoint — o envías una lista vacía — el valor almacenado es ["*"].


Requisitos y garantías de entrega

  • Solo HTTPS. La URL del endpoint debe usar https:// y no puede apuntar a hosts locales, direcciones privadas, link-local ni a la IP de metadatos de nube.
  • Secreto de una sola vista. Si no envías un secreto al crear el endpoint, el servidor genera uno (whsec_…) y lo devuelve solo en esa respuesta. Puedes rotarlo cuando quieras; la rotación también lo devuelve una única vez.
  • Reintentos. Una entrega se considera exitosa cuando tu endpoint responde con un estado 2xx. Si responde otra cosa o la conexión falla, el intento queda registrado como fallido y la cola vuelve a intentarlo; cada intento queda en el historial de entregas con su attemptNumber. Los destinos que ya recibieron con éxito una combinación (evento, origen, ocurrencia) no la vuelven a recibir en los reintentos.
  • Idempotencia. Cada transición distinta de un mismo recurso viaja con su propia occurrenceKey interna, pero un reintento puede hacer que veas el mismo evento más de una vez. Deduplica por la tupla event + sourceId + el contenido relevante de data (por ejemplo, el status en los eventos de entrega) y responde 2xx rápido — procesa en segundo plano si tu trabajo es lento.
  • Historial auditable. Cada intento queda en el registro de entregas del endpoint, consultable por la API de gestión: evento, origen, número de intento, estado y resumen del error (en un intento fallido, el resumen incluye el código con que respondió tu servidor). De la respuesta de tu servidor solo se conserva el código de estado, nunca el cuerpo.
  • Módulo requerido. El despacho exige el módulo integrations activo en la licencia de la organización.
  • Endpoints por conexión. Un endpoint puede quedar asociado a una conexión de partner mediante organizationPartnerConnectionId, para que el destino quede documentado junto a la relación que lo justifica.

¿Te sirvió esta página?