Saltar al contenido

Webhooks (respuestas)

En lugar de dejar un proceso esperando en ask(), puedes crear el grupo de preguntas con un callback_url. Cuando haya respuesta (o venza el plazo), notific.me envía la respuesta firmada a esa URL. Esas llamadas salientes son los webhooks. (Las llamadas que otros sistemas envían a notific.me son los hooks.)

const handle = await notific.createAsk({
title: "Approve refund #881?",
questions: [{ id: "ok", type: "yesno", text: "Approve?" }],
callbackUrl: "https://crm.example.com/hooks/notificme",
});
// handle.id, handle.status === "open"

Un POST a tu callback_url con la respuesta como cuerpo JSON y estas cabeceras:

Notificme-Signature: t=<unix>,kid=<key id>,sig=<base64url(Ed25519(t + "." + body))>
Notificme-Webhook-Id: <id> (igual en cada reintento: úsalo para deduplicar)
Notificme-Topic: reply
Notificme-Attempt: 1
  • La respuesta y el webhook se escriben en la misma transacción (outbox transaccional), así la decisión nunca se pierde.
  • Cualquier 2xx cuenta como entregado. Los errores y los timeouts (15 s) se reintentan con backoff exponencial durante hasta 24 horas.
  • Webhooks en app.notific.me lista cada entrega con su estado, intentos y último error, y permite reenviar.

El servidor firma con Ed25519. Las claves públicas están en <api>/.well-known/notificme-keys.json (JWKS). Se rechazan las firmas con más de 5 minutos de diferencia con tu reloj.

import express from "express";
import { NotificMe, expressWebhook, expressWebhookOf } from "@notificme/sdk";
const notific = new NotificMe();
const app = express();
// Put the middleware before any JSON parser (it reads the raw body).
app.post("/hooks/notificme", expressWebhook({ key: notific.keysUrl }), (req, res) => {
const webhook = expressWebhookOf(req)!; // verified
console.log(webhook.reply?.status, webhook.reply?.asText);
res.sendStatus(204);
});

Todos los helpers responden 401 {"error":{"code":"invalid_signature"}} cuando la firma no es válida, así tu handler nunca ve esa petición.

import { receiveWebhook, verifyWebhook } from "@notificme/sdk";
// Throws WebhookVerificationError (error.reason says why).
await verifyWebhook(rawBody, headers["notificme-signature"], keys);
// Verify and parse: headers, JSON payload and, for the "reply" topic, a typed result.
const webhook = await receiveWebhook(rawBody, headers, keys, { questions });
webhook.reply?.answers.ok;

La clave puede ser la URL de las claves (se descarga y se guarda en caché), un string base64url, los bytes, un JWK o un JWKS. WebhookVerificationError.reason es missing_header, malformed_header, timestamp_out_of_tolerance, unknown_key, invalid_key o invalid_signature.