Spécification du webhook
Entrixy envoie à votre adresse une commande d’ouverture ou de fermeture et la signe. Voici le format de la requête et la façon de vérifier la signature, pour accepter la commande dans votre propre service, maison connectée ou carte.
La requête
À chaque appui, Entrixy envoie un POST vers votre webhook_url avec un corps JSON :
POST <your webhook_url>
Content-Type: application/json
{
"action": "open",
"object_id": 42,
"timestamp": 1750000000,
"nonce": "0011223344556677",
"signature": "9d7b58066094fa88..."
}
| Champ | Valeur |
|---|---|
action | "open", pour un objet Ouvrir / fermer aussi "close" |
object_id | l'identifiant de l'objet dans votre compte |
timestamp | heure unix d'envoi, en secondes |
nonce | 16 caractères hexadécimaux (8 octets aléatoires), à usage unique |
signature | HMAC-SHA256, voir plus bas |
Un objet Ouvrir / fermer alterne selon sa dernière position connue : close s’il est ouvert, sinon open. En mode serveur, c’est le serveur qui décide ; en mode téléphone, le téléphone du propriétaire.
La signature
Ce qui est signé, c'est une chaîne canonique de quatre champs séparés par des points, et non le corps JSON lui-même : vous n'avez donc pas à reproduire notre sérialisation octet par octet :
base = "<timestamp>.<nonce>.<action>.<object_id>" signature = HMAC-SHA256(webhook_secret, base) // hex
L’application crée webhook_secret d’elle-même lorsque vous ajoutez un objet Webhook : 32 caractères hexadécimaux aléatoires. Copiez-le avec le bouton à côté du champ dans les réglages de votre récepteur. Vous pouvez le remplacer par le vôtre. Un webhook sans secret ne peut être enregistré dans aucun des deux modes, chaque requête est donc signée.
Les invités ne reçoivent jamais le secret : leurs appuis sont signés par le téléphone du propriétaire ou par le serveur, selon celui qui envoie la requête.
Comment la vérifier (de votre côté)
- Construisez la même chaîne
base = timestamp.nonce.action.object_idà partir des champs reçus. - Calculez
HMAC-SHA256(webhook_secret, base)et comparez-le avecsignatureavec une comparaison à temps constant (hash_equals/hmac.compare_digest). - Anti-rejeu : rejetez la requête si
|now − timestamp| > 300secondes. Facultativement, conservez les derniersnoncevaleurs et rejetez les répétitions. - La signature correspond et la requête est fraîche — exécutez l'ouverture.
openssl rand -hex 32. Ne mettez jamais le secret dans l’adresse elle-même.Code de vérification
Chaque exemple vérifie la signature et l’heure. Conservez le secret dans les réglages de votre récepteur ou dans une variable d’environnement.
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;
}
Pour refuser aussi une répétition dans les cinq minutes, mémorisez les valeurs nonce jusqu’à ce que leur timestamp date de plus de 300 secondes, et refusez les répétitions.
Vecteur de test
Faites tourner votre vérification sur ces valeurs et contrôlez la signature :
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 réponse
Pour que l’application affiche un résultat, répondez en JSON :
{"level":"success","message":"Opened","position":"open","close_in":30}
| Champ | Valeur |
|---|---|
level | success, warning, danger ou info : la façon dont l’application signale le résultat |
message | le texte que l’application affiche à côté de l’appui |
position | pour un objet Ouvrir / fermer : open, closed ou unknown |
close_in | secondes avant que le récepteur ne ferme de lui-même, 3600 au plus : une fois écoulées, l’application affiche l’objet fermé |
L’ancienne réponse avec le champ status (ok ou error) est également comprise. Sans l’un ni l’autre, c’est le code HTTP qui décide : toute réponse 2xx est un succès, tout le reste un échec. Un 200 OK vide suffit donc.
Sans close_in, la position est affichée deux secondes après la réponse, puis devient inconnue : le prochain appui ouvre.
Envoi
La requête est toujours un POST avec un corps JSON, jamais un simple GET.
- Depuis votre téléphone : une redirection vers le même hôte est suivie, par exemple de http vers https ou vers l’adresse avec une barre oblique finale.
- Depuis le serveur Entrixy : les redirections ne sont pas suivies et les adresses des réseaux privés et internes sont refusées.
Un récepteur accessible uniquement sur votre réseau domestique ne fonctionne donc que lorsque c’est le téléphone du propriétaire qui envoie la requête, et seulement tant que le téléphone est sur ce réseau. Pour un récepteur accessible depuis internet, utilisez https.
Essayer sans votre propre récepteur
Ouvrez la barrière de démonstration : elle vous donne une adresse de test personnelle. Indiquez-la comme adresse de l’objet et collez le secret de l’objet dans le champ de cette page — la signature de chaque appui est vérifiée dans votre navigateur, et le secret n’est envoyé nulle part.
Récepteurs tout prêts
curl envoie une requête de test avec une signature correcte. Flask est un récepteur qui vérifie la signature et refuse les répétitions. Home Assistant reçoit la commande par ce récepteur, ou par son propre déclencheur webhook sans vérification de signature.
Les relais Shelly et Tasmota ainsi qu’openHAB ne vérifient pas la signature. Ils se connectent via le client Node.js : il maintient lui-même la connexion avec Entrixy et transmet la commande à l’intérieur de votre réseau.