Documentación / Webhooks salientes

Webhooks salientes

Un webhook saliente es un mensaje que el panel envía a una dirección suya cuando ocurre algo en una cuenta de hosting, para que otro sistema pueda reaccionar: su propia facturación, un canal de chat, una hoja de cálculo o un script de aprovisionamiento.

Los webhooks están disponibles para administradores y revendedores. Un administrador recibe avisos de todas las cuentas. Un revendedor recibe avisos solo de las cuentas que le pertenecen. Los clientes y los tokens de API no pueden crearlos: los webhooks se configuran en el propio panel, en la sesión de cada persona, para que un token filtrado nunca pueda convertirse en un canal permanente con los eventos del panel.

Añadir uno

Abra la página de seguridad de su cuenta (un administrador: Seguridad; un revendedor: Seguridad de acceso) y busque Webhooks debajo de los tokens de API.

  1. Escriba la dirección, por ejemplo https://example.com/hooks/panel.
  2. Marque los eventos que desea.
  3. Pulse Añadir webhook.

El panel muestra un secreto de firma (empieza por whsec_) una sola vez. Cópielo ahora. Se guarda cifrado y no puede volver a mostrarse; si lo pierde, elimine el webhook y añada uno nuevo.

Cada persona puede tener como máximo 10 webhooks.

La dirección

  • Debe empezar por https://. Se rechaza http:// a secas, porque la llamada lleva datos de la cuenta.
  • No debe contener nombre de usuario ni contraseña. Compruebe la firma en su lugar.
  • Puede tener como máximo 500 caracteres.
  • Debe estar en internet público. Se rechazan las direcciones de este servidor, de una red privada, de la interfaz loopback o del rango de metadatos del proveedor de nube, tanto al guardar la dirección como de nuevo en el momento en que el panel se conecta (así que tampoco funciona un nombre de host que más tarde apunte a una dirección privada).
  • No se siguen las redirecciones. Indique la dirección final.

Los eventos

Evento Se envía cuando
account.created Se crea una cuenta de hosting
account.suspended Se suspende una cuenta
account.unsuspended Se reactiva una cuenta suspendida
account.deleted Se elimina una cuenta
account.activity Otra actividad en una cuenta

Un botón Llamada de prueba en cada webhook envía un evento llamado ping, para que pueda comprobar que su dirección responde. Solo puede haber una prueba en espera a la vez.

Qué se envía

Cada llamada es un POST HTTP con un cuerpo JSON:

{
  "event": "account.created",
  "at": "2026-10-23T08:15:00Z",
  "account": { "id": 42, "username": "example", "domain": "example.com" },
  "detail": "short text about what happened"
}

at es una hora UTC. El evento ping tiene la forma {"event":"ping","at":"…","webhook_id":7}.

Estas cabeceras acompañan a cada llamada:

Cabecera Significado
X-Nimbopanel-Event El nombre del evento, por ejemplo account.created
X-Nimbopanel-Delivery Un número que identifica este envío. Un reintento del mismo envío conserva el mismo número, así que úselo para ignorar duplicados
X-Nimbopanel-Timestamp Cuándo se firmó la llamada, en segundos Unix
X-Nimbopanel-Signature sha256= seguido de la firma (abajo)
Content-Type application/json
User-Agent Nimbopanel-Webhook/1

Comprobar la firma

Cualquiera puede enviar una petición a su dirección. La firma es la forma de saber que una llamada viene de su panel.

La firma es un HMAC-SHA256, en hexadecimal, de la marca de tiempo, un punto y el cuerpo en bruto exactamente como se recibió, calculado con su secreto de firma:

X-Nimbopanel-Signature == "sha256=" + hex( HMAC_SHA256( secret, X-Nimbopanel-Timestamp + "." + body ) )

Compruébela en este orden:

  1. Calcule el valor anterior a partir del cuerpo en bruto de la petición (antes de cualquier análisis o reformateo del JSON).
  2. Compárelo con la cabecera mediante una comparación de tiempo constante.
  3. Rechace la llamada si la marca de tiempo difiere en más de 5 minutos de su reloj. Así se evita que alguien reproduzca una llamada antigua.
  4. Use X-Nimbopanel-Delivery para ignorar un envío que ya haya procesado.

Ejemplo en PHP:

$body = file_get_contents('php://input');
$ts   = $_SERVER['HTTP_X_NIMBOPANEL_TIMESTAMP'] ?? '';
$sig  = $_SERVER['HTTP_X_NIMBOPANEL_SIGNATURE'] ?? '';
$want = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($want, $sig) || abs(time() - (int) $ts) > 300) {
    http_response_code(401);
    exit;
}

Ejemplo en Node.js:

const crypto = require('crypto');
function valid(rawBody, headers, secret) {
  const ts = headers['x-nimbopanel-timestamp'] || '';
  const want = 'sha256=' + crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
  const got = headers['x-nimbopanel-signature'] || '';
  return got.length === want.length
    && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want))
    && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
}

Respuesta, reintentos y pausa

  • Responda con cualquier estado 2xx en menos de 10 segundos. Cualquier otra cosa cuenta como un fallo.
  • Una llamada fallida se vuelve a intentar al cabo de 1 minuto, luego a los 5 minutos, 30 minutos y 2 horas. En total son cinco intentos, repartidos en unas dos horas y media, y después el envío se marca como fallido.
  • Las llamadas a una misma dirección se envían de una en una, empezando por la más antigua. Un envío que espera un reintento no retiene a los más recientes, así que no cuente con que los eventos lleguen en orden estricto. Una dirección lenta solo se retiene a sí misma.
  • Tras 50 envíos fallidos seguidos, el webhook se pone en pausa: ya no se encolan nuevos eventos para él. Una Llamada de prueba correcta borra el recuento y los eventos vuelven a fluir.
  • Como máximo 200 llamadas sin entregar esperan por cada webhook. Más allá de eso, no se encolan más eventos para él hasta que algunos se hayan entregado o hayan fallado.
  • El panel conserva los 50 envíos terminados más recientes de cada webhook, con la respuesta que obtuvo, para que pueda ver qué falló. Los más recientes aparecen listados bajo el webhook.

Si el panel muestra un motivo en lugar de un estado, será uno de estos: la dirección no está en internet público, no hubo respuesta en 10 segundos, el nombre de host no existe, el certificado HTTPS no es válido o el panel no pudo conectar.

Buenas costumbres

  • Responda rápido con 200 y haga el trabajo de verdad después. Un receptor que tarda más de 10 segundos parece un fallo y recibe el mismo evento de nuevo.
  • Procese dos veces el mismo envío sin que cause daño. Los reintentos y los errores de red hacen posibles los duplicados.
  • Mantenga el secreto fuera de su repositorio de código. Si se filtra, elimine el webhook y añada uno nuevo.

Relacionado: Guía para revendedores · Solución de problemas