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
- Pojdite na Obvestila → Dodaj kanal → Webhook in prilepite svoj endpoint. Biti mora
httpsin javno dosegljiv. Na zasebne ali lokalne naslove ne pošiljamo in preusmeritvam ne sledimo. - Vaša Skrivnost za podpis (
whsec_…) se prikaže takoj. Shranite jo pri sebi. - 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.