SelyGuard
La télésurveillance intelligente

Notice d'intégration

Protocole SelyPass → SelyGuard

SelyPass envoie chaque événement au centre SelyGuard par une requête HTTPS signée (JSON + HMAC-SHA256). Il n'y a ni récepteur à héberger, ni trame à coder : l'alarme arrive en moins d'une seconde avec le nom du travailleur, son téléphone et sa position GPS.

1. Activation depuis SelyPass (automatique)

Dans SelyPass : Gardiennage › Alertes › Télésurveillance › Télésurveillance SelyGuard › Activer. L'organisation remplit sa fiche (nom, adresse, consignes, contacts à appeler après l'agent). SelyPass crée alors le client et le site dans SelyGuard par l'API partenaire ci-dessous, reçoit la clé et le secret de ses alarmes, et les utilise sans aucune saisie. « Modifier » renvoie la fiche ; « Résilier » révoque la clé et garde l'historique.

PUT https://selyguard-telesurveillance-web.vercel.app/api/partner/v1/accounts/{id organisation SelyPass}
X-SelyGuard-Partner: selypass
X-SelyGuard-Timestamp: 1793548800
X-SelyGuard-Signature: hex( HMAC-SHA256( secret partenaire, timestamp + "." + "PUT /api/partner/v1/accounts/{id}" + "." + corps ) )

{ "organization": { "name": "Atlas Sécurité", "email": "contact@atlas.fr" },
  "site": { "name": "Atlas — agents PTI", "address": "…", "city": "Lyon" },
  "instructions": "Appeler l'agent puis le chef de poste…",
  "contacts": [ { "name": "Chef de poste", "phone": "+33611111111", "sms": true } ],
  "supervisionMinutes": 130 }

→ 201 / 200 { "siteId", "accountNumber": "SP-482913", "alarmEndpoint", "key", "secret", "created" }
GET    …/accounts/{id}   état : dernier signal, alarmes en cours
DELETE …/accounts/{id}   résiliation

Une organisation SelyPass correspond à un client et un site SelyGuard. Les contacts envoyés remplacent la liste précédente. Le secret partenaire est la variable SELYPASS_PARTNER_SECRET de SelyGuard et SELYGUARD_PARTNER_SECRET de SelyPass.

2. En-têtes de la requête

POST https://selyguard-telesurveillance-web.vercel.app/api/ingest/selypass
Content-Type: application/json
X-Selypass-Key: sp_live_xxxxxxxxxxxxxxxxxxxxxxxx
X-Selypass-Timestamp: 1793548800        (secondes Unix, tolérance ±5 min)
X-Selypass-Signature: hex( HMAC-SHA256( secret, timestamp + "." + corps_brut ) )

La signature se calcule sur le corps exact envoyé (mêmes octets, sans reformatage).

3. Événements PTI (travailleur isolé)

ÉvénementtypePriorité
Alerte travailleur isolélone_worker_alertP1
Homme à terreman_downP1
Travailleur isolé : pas de réponselone_worker_no_responseP1
Ronde en retardpatrol_lateP2
Fin de poste oubliéeshift_end_missedP2
Poste non prisshift_not_startedP3
Test manueltest_manualinfo (archivé)
Test cycliquetest_periodicinfo (archivé)

4. Corps JSON

{
  "eventId": "a7c1e0f2-…",              // obligatoire, unique par envoi (UUID) : anti-doublon
  "type": "man_down",                    // voir le tableau § 3
  "worker": {
    "id": "W42",                         // identifiant stable du travailleur
    "name": "Jean Dupont",
    "phone": "+33611223344"              // appelé en premier par le centre
  },
  "location": {
    "lat": 48.85661, "lng": 2.35222,
    "accuracy": 12,                      // mètres
    "address": "12 rue de Rivoli, Paris",// optionnel
    "at": "2026-11-02T08:14:05+01:00"    // heure du point GPS
  },
  "occurredAt": "2026-11-02T08:14:07+01:00",
  "message": "Chute détectée, pas de mouvement depuis 30 s",
  "device": { "id": "SP-APP-9F3A", "name": "Android – Jean" },
  "restore": false                       // true = fin d'alarme
}

Fin d'alarme

{ "eventId": "b81d…", "type": "man_down", "restore": true, "worker": { "id": "W42" } }

La fin d'alarme est rattachée à l'alerte ouverte du même travailleur (worker.id) et du même type. Elle est inscrite dans l'historique, mais l'opérateur clôture lui-même la fiche.

Test cyclique

{ "eventId": "c02e…", "type": "test_periodic", "device": { "id": "SP-GW-01" } }

5. Réponses et nouvel essai

202Alarme créée : { alarmId, escalated }terminé
200Doublon (eventId déjà reçu)terminé
401Clé, signature ou horloge invalidecorriger la configuration ; ne pas réessayer en boucle
422JSON invalide (détail dans « issues »)corriger le message
5xx / délai dépasséIncident réseau ou serveurréessayer avec le même eventId : 5 s, 15 s, 30 s, puis toutes les minutes

Réutiliser le même eventId lors d'un nouvel essai est sans risque : SelyGuard ne crée jamais deux fois la même alarme. Si le centre reste injoignable, SelyPass doit garder ses alertes et appliquer son mode dégradé (appel direct des secours).

6. Exemples (alarmes)

Node.js

import { createHmac, randomUUID } from "node:crypto";

export async function sendToSelyGuard(event) {
  const body = JSON.stringify({ eventId: randomUUID(), ...event });
  const ts = Math.floor(Date.now() / 1000).toString();
  const sig = createHmac("sha256", process.env.SELYGUARD_SECRET).update(`${ts}.${body}`).digest("hex");
  const res = await fetch("https://selyguard-telesurveillance-web.vercel.app/api/ingest/selypass", {
    method: "POST",
    headers: { "content-type": "application/json", "x-selypass-key": process.env.SELYGUARD_KEY,
               "x-selypass-timestamp": ts, "x-selypass-signature": sig },
    body,
    signal: AbortSignal.timeout(10_000),
  });
  if (res.status >= 500) throw new Error("SelyGuard indisponible, réessayer");
  return res.json();
}

await sendToSelyGuard({ type: "lone_worker_alert",
  worker: { id: "W42", name: "Jean Dupont", phone: "+33611223344" },
  location: { lat: 48.85661, lng: 2.35222, accuracy: 12 } });

Ligne de commande (test)

BODY='{"eventId":"test-001","type":"test_manual"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SELYGUARD_SECRET" -hex | sed 's/^.* //')
curl -X POST https://selyguard-telesurveillance-web.vercel.app/api/ingest/selypass \
  -H "Content-Type: application/json" -H "X-Selypass-Key: $SELYGUARD_KEY" \
  -H "X-Selypass-Timestamp: $TS" -H "X-Selypass-Signature: $SIG" -d "$BODY"

Autres types acceptés

Pour les équipements SelypassId de contrôle d'accès, de détection et de vidéo :

intrusion P2forced_door P2door_held_open P3access_denied P3fire P1smoke P1panic P1duress P1medical P1gas P1water P3temperature P3tamper P2video_motion P2technical P4power_loss P4low_battery P4offline P4arm P5disarm P5test P5other P3

Champs optionnels supplémentaires : severity (critical | high | medium | low | info, qui remplace la priorité), zone, label, metadata, et media (jusqu'à 5 images, en URL https ou en base64).

Une solution Selykai Solutions