Especificación del webhook
Entrixy envía a su dirección una orden de abrir o cerrar y la firma. Aquí tiene el formato de la petición y cómo verificar la firma, para aceptar la orden en su propio servicio, hogar inteligente o placa.
La petición
Al pulsar, Entrixy envía un POST a tu webhook_url con un cuerpo JSON:
POST <your webhook_url>
Content-Type: application/json
{
"action": "open",
"object_id": 42,
"timestamp": 1750000000,
"nonce": "0011223344556677",
"signature": "9d7b58066094fa88..."
}
| Campo | Valor |
|---|---|
action | "open", para un objeto Abrir / cerrar también "close" |
object_id | el id del objeto en tu cuenta |
timestamp | hora unix de envío, en segundos |
nonce | 16 caracteres hex (8 bytes aleatorios), de un solo uso |
signature | HMAC-SHA256, ver más abajo |
Un objeto Abrir / cerrar alterna según su última posición conocida: close cuando está abierto, si no open. En el modo servidor decide el servidor; en el modo teléfono, el teléfono del propietario.
La firma
Lo que se firma es una cadena canónica de cuatro campos separados por puntos, no el cuerpo JSON en sí, así que no tienes que reproducir nuestra serialización byte a byte:
base = "<timestamp>.<nonce>.<action>.<object_id>" signature = HMAC-SHA256(webhook_secret, base) // hex
La aplicación crea webhook_secret por sí misma al añadir un objeto Webhook: 32 caracteres hexadecimales aleatorios. Cópielo con el botón junto al campo en la configuración de su receptor. Puede sustituirlo por uno propio. Un webhook sin secreto no se puede guardar en ninguno de los dos modos, así que toda petición va firmada.
Los invitados nunca reciben el secreto: sus pulsaciones las firma el teléfono del propietario o el servidor, según quién envíe la petición.
Cómo verificarla (en tu lado)
- Construye la misma cadena
base = timestamp.nonce.action.object_ida partir de los campos recibidos. - Calcula
HMAC-SHA256(webhook_secret, base)y compáralo consignaturecon una comparación de tiempo constante (hash_equals/hmac.compare_digest). - Antirrepetición: rechaza la petición si
|now − timestamp| > 300segundos. Opcionalmente, guarda los últimosnoncevalores y rechaza las repeticiones. - Si la firma cuadra y la petición es reciente, ejecuta la apertura.
openssl rand -hex 32. Nunca ponga el secreto en la propia dirección.Código de verificación
Cada ejemplo comprueba la firma y la hora. Guarde el secreto en la configuración de su receptor o en una variable de entorno.
PHP
$secret = getenv('ENTRIXY_WEBHOOK_SECRET');
$b = json_decode(file_get_contents('php://input'), true);
$base = $b['timestamp'] . '.' . $b['nonce'] . '.' . $b['action'] . '.' . $b['object_id'];
$ok = hash_equals(hash_hmac('sha256', $base, $secret), (string)($b['signature'] ?? ''))
&& abs(time() - (int)$b['timestamp']) <= 300;
if (!$ok) { http_response_code(401); exit; }
// $b['action'] is "open" or "close"
Python
import hashlib, hmac, os, time
def verify(b: dict) -> bool:
secret = os.environ["ENTRIXY_WEBHOOK_SECRET"].encode()
base = f'{b["timestamp"]}.{b["nonce"]}.{b["action"]}.{b["object_id"]}'
mac = hmac.new(secret, base.encode(), hashlib.sha256).hexdigest()
return (hmac.compare_digest(mac, str(b.get("signature", "")))
and abs(time.time() - int(b["timestamp"])) <= 300)
Node.js
const crypto = require('crypto');
function verify(b, secret) {
const base = `${b.timestamp}.${b.nonce}.${b.action}.${b.object_id}`;
const mac = crypto.createHmac('sha256', secret).update(base).digest('hex');
const sig = String(b.signature || '');
return sig.length === mac.length
&& crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(sig))
&& Math.abs(Date.now() / 1000 - b.timestamp) <= 300;
}
Para rechazar también una repetición dentro de los cinco minutos, recuerde los valores nonce hasta que su timestamp tenga más de 300 segundos de antigüedad, y rechace las repeticiones.
Vector de prueba
Pasa tu verificación por estos valores y comprueba la firma:
webhook_secret = webhook_secret_example_key timestamp = 1750000000 nonce = 0011223344556677 action = open object_id = 42 base = 1750000000.0011223344556677.open.42 signature = 9d7b58066094fa88acc0afa7aa805fc5f0d008d33cb11b8aa2366102da8e20f1 = HMAC-SHA256(webhook_secret, base)
La respuesta
Para que la app muestre un resultado, responda con JSON:
{"level":"success","message":"Opened","position":"open","close_in":30}
| Campo | Valor |
|---|---|
level | success, warning, danger o info: cómo marca la app el resultado |
message | el texto que la app muestra junto a la pulsación |
position | para un objeto Abrir / cerrar: open, closed o unknown |
close_in | segundos hasta que el receptor cierre por sí mismo, como máximo 3600: al agotarse, la app muestra el objeto cerrado |
También se entiende la respuesta anterior con el campo status (ok o error). Sin ninguno de los dos campos decide el código HTTP: cualquier respuesta 2xx es un éxito y todo lo demás un fallo. Así que basta con un 200 OK vacío.
Sin close_in, la posición se muestra dos segundos después de la respuesta y luego pasa a ser desconocida: la siguiente pulsación abre.
Envío
La petición es siempre un POST con cuerpo JSON, nunca un GET simple.
- Desde su teléfono: se sigue una redirección al mismo host, por ejemplo de http a https o a la dirección con barra final.
- Desde el servidor de Entrixy: no se siguen redirecciones y se rechazan las direcciones de redes privadas e internas.
Por eso, un receptor accesible solo en su red doméstica funciona únicamente cuando la petición la envía el teléfono del propietario, y solo mientras el teléfono esté en esa red. Para un receptor accesible desde internet, use https.
Pruébelo sin receptor propio
Abra la barrera de demostración: le da una dirección de prueba personal. Póngala como dirección del objeto y pegue el secreto del objeto en el campo de esa página: la firma de cada pulsación se comprueba en su navegador y el secreto no se envía a ningún sitio.
Receptores ya hechos
curl envía una petición de prueba con una firma correcta. Flask es un receptor que comprueba la firma y rechaza las repeticiones. Home Assistant recibe la orden a través de ese receptor o de su propio disparador de webhook sin comprobar la firma.
Los relés Shelly y Tasmota y openHAB no comprueban la firma. Se conectan a través de el cliente en Node.js: mantiene su propia conexión con Entrixy y transmite la orden dentro de su red.