MoreLogin puede enviar por POST un evento JSON al endpoint HTTPS que tú controlas en cuanto una operación de cloud phone o cloud browser alcanza su resultado final, así que ya no tienes que hacer polling para conocerlo. Se almacena una sola URL de callback por equipo, y la Local API y la Open API escriben en el mismo registro.
Ambos entornos exponen la misma operación. Open API:
curl -X POST "https://api.morelogin.com/webhook/config" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"callbackUrl": "https://example.com/morelogin/webhook",
"enabled": true
}'Local API:
curl -X POST "http://127.0.0.1:40000/api/webhook/config" \
-H "Content-Type: application/json" \
-d '{
"callbackUrl": "https://example.com/morelogin/webhook",
"enabled": true
}'La ruta de la Local API la sirve el cliente de escritorio, así que necesita el cliente de MoreLogin v2.66.0 o superior. El endpoint de la Open API no depende del cliente.
teamId nunca se lee de la petición. Cada llamada configura el equipo del contexto del servidor, así que no puedes dirigir los eventos de otro equipo a tu endpoint.
La URL debe ser HTTPS, de 1 a 1024 caracteres, con host, sin información de usuario, sin fragmento y con un puerto válido. Todas las direcciones a las que resuelva tienen que ser enrutables públicamente: los rangos de localhost, privados, link-local, CGNAT, metadatos de nube, reservados por protocolo, de documentación y 6to4 se rechazan con 41001. La misma comprobación se repite cuando una entrega abre su conexión, así que un nombre de host que más tarde resuelva a un rango privado hace fallar esa entrega en lugar de llegar a tu red.
El guardado es un upsert por equipo, así que enviar el mismo cuerpo dos veces es inofensivo.
enabled: false mantiene la URL guardada y detiene la creación de nuevos eventos. Los eventos ya en cola se cancelan uno a uno, en el momento en que a cada uno le tocaría autorizarse, y solo mientras la configuración siga desactivada: si la reactivas pronto, los eventos en cola continúan en lugar de descartarse.
Cada entrega es un POST con estas cabeceras:
| Cabecera | Valor |
|---|---|
Content-Type | application/json |
User-Agent | MoreLogin-Webhook/1.0 |
X-Webhook-Event-Id | Igual que eventId en el cuerpo |
X-Webhook-Event-Type | Igual que eventType en el cuerpo |
Las dos cabeceras X-Webhook-* duplican campos del cuerpo a propósito, para que un router o una cola puedan repartir con ellas sin analizar el JSON.
{
"eventId": "c7f3a2b10d8e4f6a9c1b2d3e4f5a6b7c",
"eventType": "cloud_phone.power_off",
"eventTime": "1787241600000",
"data": {
"teamId": "10001",
"cloudPhoneId": "20001",
"reason": "MONEY_SAVING"
},
"result": {
"success": true,
"code": "0",
"message": null
}
}| Campo | Descripción |
|---|---|
eventId | ID de evento único. Los reintentos y las repeticiones internas del mismo evento lo reutilizan, así que es la clave de idempotencia |
eventType | Nombre de evento estable, de la tabla siguiente |
eventTime | Cuándo ocurrió el resultado final, en milisegundos de época Unix. Se envía como cadena |
data.teamId | Equipo propietario del recurso. Se envía como cadena |
data.cloudPhoneId | Cloud phone al que se refiere el evento. Se envía como cadena |
data.cloudBrowserId | Perfil de cloud browser al que se refiere el evento. Se envía como cadena |
data.runId | Instancia de ejecución de cloud browser a la que se refiere el evento |
data.reason | Solo en cloud_phone.power_off y cloud_browser.stop |
result.success | Si la operación finalmente tuvo éxito |
result.code | 0 en caso de éxito; si no, un código de error de negocio estable que no depende del idioma |
result.message | null en caso de éxito; si no, una explicación en el idioma del equipo, con reserva a en-US |
Todos los identificadores numéricos y eventTime llegan como cadenas JSON, igual que en el resto de la API: consulta Formato común de respuesta. Interprétalos como cadenas y no como números, o perderás precisión en los ID de 64 bits.
eventType | Resultado final que informa |
|---|---|
cloud_phone.power_on | Encendido correcto o fallido |
cloud_phone.power_off | Apagado correcto. Un apagado fallido no produce evento |
cloud_phone.restart | Reinicio correcto o fallido |
cloud_phone.reset | Restablecimiento correcto o fallido |
cloud_phone.new_machine | Nuevo dispositivo con un clic correcto o fallido |
cloud_browser.start | Inicio del cloud browser correcto o fallido |
cloud_browser.stop | Cada intento de cierre del cloud browser, con éxito o sin él |
Los eventos de cloud phone llevan teamId y cloudPhoneId. Los de cloud browser llevan teamId, cloudBrowserId y runId.
El encendido, el reinicio, el restablecimiento y el nuevo dispositivo con un clic informan success: true solo una vez que la devolución de llamada del servicio tuvo éxito y la sincronización local se confirmó. Un fallo de facturación, o una comprobación de proxy asíncrona que falla y completa su limpieza, informa de un encendido fallido y no emite además un apagado. El trabajo diferido, como instalar aplicaciones preinstaladas o limpiar archivos caducados, no cambia el resultado.
Si un apagado interrumpe un arranque, ese intento no produce evento de encendido; el apagado correcto posterior sí produce su propio cloud_phone.power_off.
cloud_phone.power_off añade data.reason:
| Valor | Significado |
|---|---|
NORMAL | Apagado ordinario |
MONEY_SAVING | Modo de ahorro de dinero |
OTHER | Impagos, caducidad, mantenimiento o cualquier otra causa |
cloud_browser.stop añade data.reason:
| Valor | Significado |
|---|---|
NORMAL | Cierre ordinario |
MONEY_SAVING | Modo de ahorro de dinero |
ENERGY_SAVING | Modo de ahorro de energía |
BROWSER_EXITED | El navegador se cerró de forma inesperada |
OTHER | Cualquier otra causa |
Un fallo interno de archivado solo cambia result; nunca sobrescribe el reason capturado cuando comenzó el cierre.
Cualquier 2xx cuenta como entregado. Cualquier otro estado, más los tiempos de espera de conexión y de lectura y los errores de red, cuenta como fallo. El tiempo de espera de conexión es de 5 segundos y el de lectura, de 30 segundos.
Una entrega fallida se reintenta como máximo 6 veces, a los 10 segundos, 30 segundos, 60 segundos, 3 minutos, 10 minutos y 30 minutos. Son como máximo 7 peticiones para un evento. Cuando falla el sexto reintento, el evento deja de reintentarse automáticamente.
Los eventos de un mismo cloud phone, o de un mismo cloud browser, se entregan en orden de creación, de uno en uno. Los recursos distintos se entregan en paralelo, así que no asumas ningún orden entre ellos.
Los reintentos de un evento reutilizan siempre el mismo eventId, eventTime y cuerpo. En situaciones límite de red o de recuperación, el mismo evento puede entregarse más de una vez con esos valores sin cambios, así que deduplica por eventId.
En los cierres de cloud browser, un intento de cierre produce exactamente un evento. Un reintento manual posterior, una liberación forzada manual o una liberación forzada iniciada por el sistema son una operación nueva y reciben un eventId nuevo. Dos intentos nunca se fusionan en un solo evento.
Las operaciones de cloud phone se identifican por el ID de tarea del servicio, traceId o una marca de tiempo de negocio estable. Cuando no existe ninguno, el servicio genera un UUID y trata la devolución de llamada final actual como una operación nueva, así que recibes un eventId nuevo en lugar de que dos operaciones distintas se colapsen en una.
Devuelve un 2xx en cuanto hayas almacenado el evento, y haz el trabajo real de forma asíncrona. El cuerpo de la respuesta se ignora, y un manejador lento consume tus 30 segundos de lectura y empuja el evento al calendario de reintentos aunque lo hayas recibido.
Deduplica por eventId. Trata una repetición como ya gestionada en lugar de repetir los efectos secundarios.
No trates la carga como entrada de confianza solo porque llegó a tu endpoint. Esta versión no firma los callbacks, así que cualquiera que conozca tu URL puede publicar en ella. Usa una URL difícil de adivinar y, antes de hacer algo costoso, confirma el estado a través de la API: por ejemplo POST /cloudphone/info para un cloud phone, o POST /cloudbrowser/page para una ejecución de cloud browser.
result.code usa los mismos códigos de negocio que la API sincrónica, así que un fallo se puede consultar en Códigos de error. result.message es una cadena legible cuyo texto e idioma pueden cambiar; ramifica según result.success y result.code, nunca según result.message.
El contrato legible por máquina de cada evento, incluidos los esquemas y los ejemplos por evento, se publica con la especificación de recursos compartidos en Shared Resources Open API.