Dokumentacija

Webhooki

Kanal webhook vsako obvestilo pošlje kot JSON zahtevek POST na vaš endpoint. Vsak zahtevek je podpisan, zato lahko preverite, da res prihaja od nas.

Nastavitev

  1. Pojdite na Obvestila → Dodaj kanal → Webhook in prilepite svoj endpoint. Biti mora https in javno dosegljiv. Na zasebne ali lokalne naslove ne pošiljamo in preusmeritvam ne sledimo.
  2. Vaša Skrivnost za podpis (whsec_…) se prikaže takoj. Shranite jo pri sebi.
  3. Pošljemo testni zahtevek. Takoj ko vaš endpoint odgovori s katero koli kodo 2xx, je kanal potrjen in začne prejemati obvestila. Ob katerem koli drugem odgovoru se kanal s skrivnostjo vseeno doda kot Ni potrjeno: skrivnost dodajte na endpoint in pritisnite Ponovno preveri endpoint.

Zahtevek

POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-PulseKeeper-Timestamp: 1790479747
X-PulseKeeper-Signature: 5d41402abc4b2a76b9719d911017c592…
X-PulseKeeper-Delivery: ntf_1842
Glava Pomen
X-PulseKeeper-Timestamp Čas Unix, ko smo zahtevek podpisali.
X-PulseKeeper-Signature Šestnajstiški HMAC-SHA256 nad timestamp.body z vašo skrivnostjo. Med rotacijo skrivnosti nosi dva podpisa, ločena z vejico.
X-PulseKeeper-Delivery ID tega sporočila. Pri vsakem ponovnem poskusu ostane enak, zato lahko dvojnike prezrete.

Preverjanje podpisa

Izračunajte HMAC nad časovnim žigom, piko in surovim telesom zahtevka ter ga primerjajte z vsakim podpisom v glavi. Zavrnite zahtevke, starejše od 5 minut.

PHP

$timestamp = $request->header('X-PulseKeeper-Timestamp');
$expected  = hash_hmac('sha256', $timestamp.'.'.$request->getContent(), $secret);

$valid = abs(time() - (int) $timestamp) < 300
    && collect(explode(',', $request->header('X-PulseKeeper-Signature')))
        ->contains(fn ($signature) => hash_equals($expected, trim($signature)));

JavaScript

import crypto from 'node:crypto';

const timestamp = req.headers['X-PulseKeeper-Timestamp'.toLowerCase()];
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
const valid = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
  && req.headers['X-PulseKeeper-Signature'.toLowerCase()].split(',')
       .some((s) => crypto.timingSafeEqual(Buffer.from(s.trim()), Buffer.from(expected)));

Uporabite surovo telo, točno tako, kot ste ga prejeli: ponovno kodiranje JSON-a ga spremeni in podpis se ne bo ujemal. Uporabite primerjavo s konstantnim časom (hash_equals, timingSafeEqual).

Rotacija skrivnosti

Zamenjaj skrivnost ustvari novo skrivnost. Naslednjih 24 ur vsak zahtevek podpišemo z novo in staro skrivnostjo, zato lahko novo uvedete, ne da bi zamudili obvestilo.

Dogodki

Polje event pove, kaj se je zgodilo, zato vašemu endpointu ni treba hraniti stanja:

Dogodek Kdaj
incident.alert Incident je potrjen (prvo obvestilo in poznejši koraki eskalacije).
incident.reminder Incident je še odprt. Opomniki se redčijo, glejte pragove in opomnike.
incident.recovery Incident je razrešen. Pošlje se na vse kanale, ki so dobili obvestilo. Izjema je certifikat ali domena, podaljšana pred potekom: e-pošta in klepet ostaneta tiho, webhook pa ta dogodek vseeno dobi, da lahko zaprete, kar ste odprli ob incident.alert. Kateri primer je, pove incident.type (ssl_expiring, domain_expiring).
monitor.never_connected Nova tarča še nikoli ni odgovorila, običajno zaradi tipkarske napake ali požarnega zidu.
deployment.regressed Deploy je bil opazno počasnejši ali težji kot v uri pred njim.
notifications.batch Več dogodkov, združenih v eno sporočilo (tihe ure ali veliko obvestil naenkrat).
channel.test Kliknili ste Pošlji test. Ob gumbu lahko izberete tudi katerikoli dogodek zgoraj: dobite njegov vzorec z "test": true na vrhu in izmišljenim monitorjem na shop.example.com.

Dogodek incidenta je videti takole:

{
  "event": "incident.alert",
  "incident": {
    "id": 128,
    "type": "downtime",
    "severity": "critical",
    "state": "confirmed",
    "started_at": "2026-09-27T12:24:02+00:00",
    "confirmed_at": "2026-09-27T12:25:02+00:00",
    "resolved_at": null,
    "duration_seconds": null,
    "error_code": "http_5xx",
    "affected_locations": ["fra", "nyc"],
    "healthy_locations": [],
    "url": "http://pulsekeeper.io/domain/3f9a1c07e4b2d815?tab=incidents"
  },
  "monitor": {
    "id": 42,
    "type": "http",
    "name": "shop.example.com",
    "hostname": "shop.example.com",
    "url": "https://shop.example.com"
  }
}

Časi so v obliki ISO 8601 v UTC. severity je critical za izpade in warning za stvari, ki zahtevajo pozornost, a niso izpad: certifikat, ki se mu izteka veljavnost, požarni zid, ki blokira naša preverjanja, ali počasnejši deploy.

Ponovni poskusi

Če vaš endpoint ne odgovori v 8 sekundah ali vrne kar koli drugega kot 2xx, poskusimo znova čez 10 sekund in nato še čez minuto, skupaj trikrat, vsakič z isto glavo X-PulseKeeper-Delivery. Zadnja napaka je prikazana ob kanalu na strani Obvestila.