Portal Community

What the Post-Receive Hook Can Do

Cannot Block OnAfterRespond cannot prevent the response from being processed. If you throw an exception, it is caught, logged, and the cycle continues normally. Use the pre-send hook (OnBeforePublish) if you need to block.

Notification Hook Example

public class ApprovalNotificationHook : IInteractionHook
{
    private readonly IEmailService _email;
    private readonly IUserService _users;

    public Task OnBeforePublish(
        InteractionRequest request,
        CancellationToken ct) => Task.CompletedTask;

    public async Task OnAfterRespond(
        InteractionRequest request,
        InteractionResponse response,
        CancellationToken ct)
    {
        // Only handle approval interactions
        if (request.Type != InteractionTypes.Approval)
            return;

        var requester = await _users.GetByCorrelationAsync(request.CorrelationId, ct);
        if (requester is null) return;

        var subject = response.Outcome == "approved"
            ? $"[Approved] {request.Title}"
            : $"[Rejected] {request.Title}";

        await _email.SendAsync(requester.Email, subject,
            body: $"Your request was {response.Outcome} by {response.RespondedBy} at {response.Timestamp}.",
            ct: ct);
    }
}

Metrics Hook Example

public class MetricsInteractionHook : IInteractionHook
{
    private readonly IMeterFactory _meterFactory;
    private readonly Histogram<double> _responseTimeHistogram;
    private readonly Counter<long> _responseCounter;

    public MetricsInteractionHook(IMeterFactory meterFactory)
    {
        var meter = meterFactory.Create("EdgeInteract");
        _responseTimeHistogram = meter.CreateHistogram<double>(
            "interaction_response_time_ms",
            unit: "ms");
        _responseCounter = meter.CreateCounter<long>(
            "interaction_responses_total");
    }

    public Task OnBeforePublish(
        InteractionRequest request,
        CancellationToken ct) => Task.CompletedTask;

    public Task OnAfterRespond(
        InteractionRequest request,
        InteractionResponse response,
        CancellationToken ct)
    {
        var responseTimeMs = (response.TimestampUtc - request.PublishedAtUtc).TotalMilliseconds;

        _responseTimeHistogram.Record(responseTimeMs,
            new TagList {
                { "interaction.type", request.Type },
                { "outcome", response.Outcome }
            });

        _responseCounter.Add(1,
            new TagList {
                { "interaction.type", request.Type },
                { "outcome", response.Outcome }
            });

        return Task.CompletedTask;
    }
}

Post-Receive Hook Best Practices

PracticeReason
Keep hooks fast (under 100ms)Hooks run synchronously in the pipeline before the ack is sent to the client
Use fire-and-forget for slow operationsQueue emails, notifications, and external calls via background jobs — don't block the hook
Handle exceptions internallyDon't let exceptions propagate — log and continue; the cycle must not fail due to a notification error
Filter by interaction typeUse early returns for types this hook doesn't handle
Use correlationId to find contextThe original business entity (workflow execution ID, entity ID) is in correlationId