# `deliverPresenceWebhook` ## Purpose Best-effort, signed delivery of a single presence event to a configured webhook receiver. The gateway calls this function on every presence transition (`join` / `leave` / `update`) when a `presenceWebhook` is configured on the daemon. Retry, exponential backoff, and circuit-breaking are owned by `@suaveplan/webhook-client.deliver`, which internally threads `@suaveplan/retry` — the gateway never has to reinvent that policy. ## Features - Per-event opt-in filter: `config.events` constrains which kinds fan out. Default (`undefined`) fans out every kind. - HMAC-SHA-256 signed payloads when `config.secret` is set, matching the proposal's "signed delivery payload" requirement. - Errors are swallowed and routed to an optional `onError(err)` hook so presence — which is inherently lossy — never crashes the daemon. - Deterministic delivery id: `presence--` makes the receiver's idempotency story easy. ## Usage: Basic Example ```ts import { deliverPresenceWebhook } from "@suaveplan/websocket-gateway"; await deliverPresenceWebhook( { url: "https://example.com/webhooks/presence", secret: "shared-secret" }, { kind: "presence:join", clientId: "alice", roomId: "ops", status: "online", at: Date.now() } ); ``` ## Usage: Advanced Example ```ts import { deliverPresenceWebhook } from "@suaveplan/websocket-gateway"; import { logger } from "@suaveplan/logger"; await deliverPresenceWebhook( { url: "https://hooks.example.com/presence", secret: process.env.PRESENCE_HMAC, events: ["presence:join", "presence:leave"], // skip 'update' chatter }, { kind: "presence:update", clientId: "alice", roomId: undefined, status: "away", at: Date.now(), }, (err) => logger.warn("presence webhook delivery failed", { err }) ); ``` ## API Reference - `deliverPresenceWebhook(config, event, onError?): Promise` - `PresenceWebhookEvent` — `{ kind, clientId, roomId, status, at }` value object. ## Implementation Notes The function never throws — `webhook-client.deliver` already classifies retryable vs terminal status codes and consumes the retry budget internally. Surfacing failures upstream would force the gateway to invent a recovery policy, but presence is best-effort by design (receivers reconcile via the next heartbeat / connection event). Delivery is signed with the same algorithm `webhook-client` defaults to (`sha256`); future stronger algorithms (e.g. ed25519) become a `config.algorithm` switch the moment `webhook-client` exposes one.