Redeon.SuperSiteEngineCore.Web.Eltheon.Core.Features.Webhooks
Webhook bridge and delivery infrastructure for Eltheon v2. This feature consumes internal Events, moves external webhook delivery onto an InMemory work queue, and keeps delivery attempts diagnosable through persisted history and admin-facing query models.
Scope: follow-up feature on top of
Redeon.SuperSiteEngineCore.Web.Eltheon.Core.Features.EventsandRedeon.SuperSiteEngineCore.Web.Eltheon.Core.Features.InMemory.
Install
dotnet add package Redeon.SuperSiteEngineCore.Web.Eltheon.Core.Features.Webhooks
DI Setup
builder.Services.AddEltheonEvents();
builder.Services.AddEltheonInMemory();
builder.Services.AddEltheonWebhooks(options =>
{
options.QueueName = "eltheon.webhooks.dispatch";
options.MaxAttempts = 3;
options.RequestTimeout = TimeSpan.FromSeconds(10);
});
AddEltheonWebhooks(...) registers the publication registry, in-memory endpoint/subscription repositories, persisted attempt history, HMAC signing, delivery services, an event bridge, an admin query service, and a hosted dispatcher worker.
Register Publications
public sealed record UserCreatedEvent(string UserId, string Email) : IEvent
{
public string EventName => "User.Created";
public DateTime OccurredAtUtc { get; } = DateTime.UtcNow;
public string? Source => "Identity";
public string? CorrelationId => null;
public string? Category => "Users";
}
public sealed class UserCreatedWebhookPublication
: WebhookPublicationDefinition<UserCreatedEvent>
{
public override string EventKey => "user.created";
public override string Version => "v1";
protected override object MapPayloadCore(UserCreatedEvent @event)
=> new { @event.UserId, @event.Email };
protected override WebhookScope? ResolveScopeCore(UserCreatedEvent @event)
=> new()
{
ScopeType = WebhookScopeType.Scoped,
ScopeKey = "tenant-42"
};
}
builder.Services.AddSingleton<IWebhookPublicationDefinition, UserCreatedWebhookPublication>();
Delivery Model
- The bridge consumes internal
IEventinstances viaIEventProvider. - Registered publication definitions translate internal events into webhook envelopes and can attach generic scope metadata.
- A
WebhookDispatchItemis enqueued into the configuredInMemoryqueue. WebhookDispatcherHostedServicedequeues jobs, resolves subscriptions/endpoints, signs requests, and delivers them throughHttpClient.- Retryable delivery failures are re-enqueued until
MaxAttemptsis reached. - Attempt history is persisted through
IWebhookAttemptRepository, following the same persistence-first idea used by the Email service pattern. IWebhookAdminQueryServiceexposes endpoints, subscriptions, queue state, and recent attempts for admin or diagnostics surfaces.
Request Shape
Outbound requests use application/json and send a default envelope:
{
"eventKey": "user.created",
"version": "v1",
"deliveryId": "8e85f8ee-fb73-45d8-baae-7d59152796f7",
"eventId": "52a3c4c1-a6d0-4767-884e-b4af6b2dc58c",
"occurredAtUtc": "2026-04-23T18:22:00Z",
"timestampUtc": "2026-04-23T18:22:01Z",
"source": "Identity",
"correlationId": "request-42",
"scope": {
"scopeType": "Scoped",
"scopeKey": "tenant-42"
},
"payload": {
"userId": "123",
"email": "hello@example.com"
}
}
Headers include delivery id, event key, version, timestamp and an HMAC signature.
Diagnostics And Admin Preparation
IWebhookAttemptRepositorypersists attempt history to the filesystem by default and keeps recent entries queryable.IWebhookAdminQueryServicereturns a diagnostics snapshot with:- registered endpoints
- active subscriptions
- current queue items
- recent delivery attempts
- Scope is modeled in the webhook domain (
WebhookScope) rather than in queue topology, so later products can map their own semantics on top.
Tests
Redeon.SuperSiteEngineCore.Web.Eltheon.Core.Features.Webhooks.Test covers bridge mapping including scope propagation, signing and headers, dispatcher retry behavior, attempt history, diagnostics snapshots, and DI registration.