Notifications
When a projection produces new state, Hapnd can push that change to your infrastructure. Notifications fire on projection state changes, not raw events — you receive the computed state, ready to use.
Three delivery mechanisms are available. Choose based on your use case, or combine them.
Webhooks
Section titled “Webhooks”Outbound HTTP POST to your endpoint with HMAC-SHA256 signatures for verification. Best for server-to-server integration where you want Hapnd to push to you reliably.
Configuration
Section titled “Configuration”Webhook configuration is provided when you upload a projection:
# Upload with notification configcurl -X POST https://hapnd-api.lightestnight.workers.dev/projections/upload \ -H "X-API-Key: your_key" \ -F "file=@projection.zip" \ -F 'notificationConfig={"webhook":{"url":"https://your-api.com/hooks/hapnd"}}'Signature Verification
Section titled “Signature Verification”Every webhook includes an HMAC-SHA256 signature in the headers. Verify it before processing:
var signature = request.Headers["X-Hapnd-Signature"];var payload = await request.ReadAsStringAsync();
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(webhookSecret));var computed = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)));
if (signature != computed){ return Results.Unauthorized();}Webhook Secrets
Section titled “Webhook Secrets”Hapnd generates webhook secrets automatically using the whsec_ prefix convention. Secrets are managed at two levels:
- Tenant-level — a shared secret used by all projections by default
- Per-projection — an isolated secret for projections that need independent rotation
Rotate secrets via the API:
# Rotate tenant-level secret (affects all projections using the shared secret)curl -X POST https://hapnd-api.lightestnight.workers.dev/webhooks/rotate \ -H "X-API-Key: your_key"
# Rotate an isolated projection secretcurl -X POST https://hapnd-api.lightestnight.workers.dev/projections/proj_abc/webhook/rotate \ -H "X-API-Key: your_key"WebSocket Streaming
Section titled “WebSocket Streaming”Real-time push via persistent WebSocket connections. Best for dashboards, live UIs, and reactive workflows where latency matters.
.NET SDK
Section titled “.NET SDK”var subscription = hapnd.Subscriptions() .OnStateChanged<OrderState>(async (update, ct) => { Console.WriteLine($"Order {update.AggregateId} total: {update.State.Total}"); }) .OnError(async (error, ct) => { error.Action.Reconnect(); }) .Subscribe();
// Later:await subscription.DisposeAsync();TypeScript SDK
Section titled “TypeScript SDK”const subscription = hapnd.subscribe({ projections: [ { id: "proj_orders", onUpdate: async (update) => { console.log(`Order ${update.aggregateId} total: ${update.state.total}`); }, }, ], onError: async (error) => { error.action.reconnect(); },});
// Later:await subscription.close();Each projection gets its own WebSocket connection with automatic reconnection and sequence tracking. See the .NET SDK and TypeScript SDK docs for full details.
REST Polling
Section titled “REST Polling”Cursor-based polling for environments where WebSockets aren’t practical or you prefer pull-based consumption.
# First request — get latest notificationscurl https://hapnd-api.lightestnight.workers.dev/projections/proj_abc/notifications \ -H "X-API-Key: your_key"
# Subsequent requests — pass the last sequence to get only new notificationscurl "https://hapnd-api.lightestnight.workers.dev/projections/proj_abc/notifications?afterSequence=42" \ -H "X-API-Key: your_key"The response includes an array of notifications, each with a sequence number. Pass the highest sequence as afterSequence on your next request to get only new changes.
Choosing a Mechanism
Section titled “Choosing a Mechanism”| Mechanism | Best for | Trade-offs |
|---|---|---|
| Webhooks | Server-to-server, reliable delivery | Hapnd manages retries and DLQ; you provide an endpoint |
| WebSocket | Real-time dashboards, reactive workflows | Requires persistent connection; SDK handles reconnection |
| Polling | Simple integrations, batch processing | You control the pace; higher latency than push |
You can use multiple mechanisms for the same projection. For example, WebSocket for your dashboard and webhooks for your billing system.
Notification Retention
Section titled “Notification Retention”Notification logs are retained for 7 days. After that, they’re archived to R2 storage and removed from the active log. The daily cleanup runs at 3am UTC.
If you need to reprocess notifications older than 7 days, the projection can be re-activated to replay from historical events.