Webhooks
Webhooks let PLUR push events at your systems instead of you polling. Useful for piping engram activity into Slack, audit warehouses, Datadog dashboards, or any other internal system.
Register
Section titled “Register”In the admin dashboard at /admin/webhooks (org-wide) or /me/webhooks (your own events):
- Click
New webhook. - Enter the target URL.
- Pick events to subscribe to (see Events below).
- PLUR generates a webhook secret — copy it; it’s used for HMAC verification.
- Optional: test-fire to verify your endpoint accepts the request.
Events
Section titled “Events”| Event | Fires when |
|---|---|
engram.created | An engram is saved. |
engram.updated | An engram’s fields are updated (status, activation, feedback). |
engram.retired | An engram moves to retired status. |
engram.pinned | An admin pins / unpins. |
pack.installed | A Knowledge Pack is installed. |
pack.uninstalled | A Pack is removed. |
pack.exported | A Pack is exported. |
session.started | A new session is created. |
session.ended | A session is closed (carries summary + new engram count). |
user.created | New user provisioned (SCIM or manual). |
user.disabled | User is disabled. |
audit.entry | Any audit row is written. High-volume — subscribe carefully. |
You can subscribe to all of them or just the ones you care about.
Payload shape
Section titled “Payload shape”Every webhook POST has this envelope:
{ "event": "engram.created", "id": "wh-evt-2026-0525-001", "ts": "2026-05-25T14:32:18.123Z", "org": "acme", "actor": { "type": "user", "id": "u_alice" }, "data": { /* event-specific payload */ }}Headers:
Content-Type: application/jsonX-PLUR-Event: engram.createdX-PLUR-Delivery: wh-dlv-2026-0525-001X-PLUR-Signature: sha256=<hex hmac>Verifying signatures
Section titled “Verifying signatures”The signature is HMAC-SHA256 over the raw request body with the webhook’s secret as the key. Verify before processing:
import crypto from 'node:crypto';
function verify(rawBody: string, signature: string, secret: string): boolean { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected), );}In Python:
import hmac, hashlib
def verify(raw_body: bytes, signature: str, secret: str) -> bool: expected = 'sha256=' + hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected)Use constant-time comparison — naive == is timing-attack-prone.
Retries
Section titled “Retries”Delivery is at-least-once. On non-2xx response or timeout:
- Retry with exponential backoff: 30 s, 1 min, 5 min, 15 min, 1 h.
- Max 5 attempts.
- If all attempts fail, webhook is marked
failing. - After 24 hours of continuous failures, the webhook is
auto-disabledand the admin gets an email.
Idempotency: every event carries an id field. Your handler should be idempotent on that ID — PLUR may deliver the same event twice during retries.
Replay protection
Section titled “Replay protection”Every delivery carries a fresh X-PLUR-Delivery ID and ts. Reject deliveries with timestamps older than 5 minutes (against your server’s clock). The signature alone doesn’t prevent replay; the timestamp does.
Auto-disable and re-enable
Section titled “Auto-disable and re-enable”When auto-disabled, the webhook stays registered but is paused. Re-enable from /admin/webhooks or /me/webhooks after fixing your endpoint. Test-fire before re-enabling — you don’t want the next real event to be the canary.
Throughput
Section titled “Throughput”Default rate: ~50 events/sec/webhook. Higher-volume orgs can raise this; talk to your PLUR contact. The dispatcher (src/webhooks/dispatcher.ts) holds a small in-process queue — if your endpoint is slow, deliveries back up, retries kick in, and eventually the webhook auto-disables. Make your endpoint fast: 200ms target, 1s ceiling.
When not to use webhooks
Section titled “When not to use webhooks”- For triggering an LLM call — webhooks are not real-time enough; round-trip is ~50ms but retries can push effective latency to seconds. Use the MCP/REST API.
- For a Slack notification on every recall — too noisy. Subscribe to
engram.created/pack.installedinstead. - For audit-grade compliance pipelines — use the audit log export instead, or subscribe to
audit.entryand periodically reconcile with a CSV export to catch missed deliveries.