Webhooks
Bijgewerkt Aug 2026 · API v3Registreer een HTTPS-endpoint en pon roept jou aan wanneer er iets gebeurt — geen polling-loop, geen verspilde requests. Beheer je webhooks op my.pon.app → Webhooks of via de API.
Topics
| Topic | Vuurt wanneer |
|---|---|
list.changed |
Er iets aan een lijst veranderde (grof, samengevoegd — de standaard) |
item.added |
Een artikel op een lijst belandde |
item.removed |
Een artikel werd verwijderd |
item.checked |
Een artikel tijdens het winkelen werd afgevinkt (het vinkje weghalen vuurt alleen list.changed) |
list.members.changed |
Iemand een lijst joinde of verliet |
Abonneer je op wat je echt nodig hebt. Een optioneel listIds-filter
(maximaal 50, alleen je eigen lijsten — onbekende ids antwoorden 400 met
invalidListIds) beperkt leveringen tot specifieke lijsten.
Registreren
POST /v3/webhooks
curl -X POST https://api.pon.app/v3/webhooks \
-H "Authorization: Bearer pon_pat_XXXX" \
-H "Content-Type: application/json" \
-d '{ "url": "https://hooks.example.com/pon",
"events": ["item.checked"],
"listIds": ["LIST_ID"] }'
De response bevat je signing-secret (whsec_…) — één keer getoond.
Dezelfde URL opnieuw registreren werkt het abonnement bij en behoudt het
secret. Maximaal 5 webhooks per account.
Registratie pingt je URL meteen.
pon stuurt een gesigneerdping-event en verwacht een 2xx — beantwoord die vóór je de signature controleert (het secret komt pas in de response). Een dode URL wordt geweigerd met 422 WEBHOOK_UNREACHABLE.Leveringen
Elke levering is een POST met de headers PON-Event: <topic> en
PON-Signature: sha256=<hmac>:
{ "event": "item.checked", "listId": "6led…", "cursor": 123, "at": "2026-08-09T19:00:00.000Z" }
Leveringen zijn best-effort, zonder retries — als je endpoint plat ligt, brengt het volgende event (of een fetch aan jouw kant) je weer bij. Redirects worden niet gevolgd. Meerdere passende wijzigingen in één schrijfactie worden samengevoegd tot één levering per geabonneerd topic.
De signature verifiëren
Bereken een HMAC-SHA256 over de rauwe request-body met je secret en vergelijk die — constant-time — met de header:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const expected = "sha256=" +
createHmac("sha256", secret).update(rawBody).digest("hex");
return (
header.length === expected.length &&
timingSafeEqual(Buffer.from(header), Buffer.from(expected))
);
}import hashlib, hmac
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(header, expected)Circuit breaker
Een 2xx bevestigt een levering. Na 20 opeenvolgende mislukkingen (al
het andere, timeouts inbegrepen) pauzeert de webhook zichzelf
(status: paused, pausedReason: auto_failures). Repareer je endpoint en
activeer hem daarna weer op my.pon.app of via PATCH /v3/webhooks/{id}
met { "status": "active" } — heractiveren zet de teller op nul. Een
handmatige testping is POST /v3/webhooks/{id}/pings
(telt nooit als mislukking).
Webhooks verslaan polling — altijd.
Een polling-loop van 5 minuten doet zo’n 8.600 requests per maand en telt op de fair-use-meter als zwaar gebruik. Een webhook doet er precies zoveel als je lijsten veranderen.