Webhooks
Actualizado Aug 2026 · API v3Registra un endpoint HTTPS y pon te llama cuando pasa algo — sin bucle de polling, sin peticiones desperdiciadas. Gestiona tus webhooks en my.pon.app → Webhooks o vía API.
Topics
| Topic | Se dispara cuando |
|---|---|
list.changed |
Cualquier cosa de una lista cambió (grueso, agrupado — el predeterminado) |
item.added |
Un artículo aterrizó en una lista |
item.removed |
Un artículo fue eliminado |
item.checked |
Un artículo fue marcado durante la compra (desmarcarlo solo dispara list.changed) |
list.members.changed |
Alguien se unió a una lista o la abandonó |
Suscríbete a lo que de verdad necesites. Un filtro opcional listIds
(hasta 50, solo tus propias listas — los ids desconocidos responden 400
con invalidListIds) restringe las entregas a listas concretas.
Registro
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"] }'
La respuesta contiene tu secreto de firma (whsec_…) — se muestra una
sola vez. Volver a registrar la misma URL actualiza la suscripción y
conserva el secreto. Hasta 5 webhooks por cuenta.
El registro hace ping a tu URL de inmediato.
pon envía un eventoping firmado y espera un 2xx — respóndelo antes de comprobar la firma (el secreto solo llega en la respuesta). Una URL muerta se rechaza con 422 WEBHOOK_UNREACHABLE.Entregas
Cada entrega es un POST con los headers PON-Event: <topic> y
PON-Signature: sha256=<hmac>:
{ "event": "item.checked", "listId": "6led…", "cursor": 123, "at": "2026-08-09T19:00:00.000Z" }
Las entregas son best-effort, sin reintentos — si tu endpoint está caído, el siguiente evento (o un fetch por tu parte) te pone al día. No se siguen redirecciones. Varios cambios coincidentes en una misma escritura se agrupan en una entrega por topic suscrito.
Verifica la firma
Calcula un HMAC-SHA256 sobre el cuerpo crudo de la petición con tu secreto y compáralo — en tiempo constante — con el 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
Un 2xx confirma una entrega. Tras 20 fallos consecutivos (cualquier
otra cosa, incluidos los timeouts) el webhook se pausa solo
(status: paused, pausedReason: auto_failures). Arregla tu endpoint y
reactívalo en my.pon.app o vía PATCH
/v3/webhooks/{id} con { "status": "active" } — reactivar pone el
contador a cero. Un ping de prueba manual es POST
/v3/webhooks/{id}/pings (nunca cuenta como fallo).
Los webhooks ganan al polling — siempre.
Un bucle de polling cada 5 minutos hace ~8.600 peticiones al mes y cuenta como uso intensivo en el indicador de fair use. Un webhook hace exactamente tantas como cambien tus listas.