EdgeInteract
Post-Receive Hook
The post-receive hook (OnAfterRespond) runs after a valid InteractionResponse has been received and validated. It cannot block the cycle, but it is the correct place for audit logging, metrics, and business side effects.
What the Post-Receive Hook Can Do
- Audit: Record the full request + response pair for compliance
- Metrics: Emit response time metrics, completion counters
- Business events: Trigger downstream effects (send an email notification, update a record) based on the response outcome
- Notifications: Notify the requester or other parties of the outcome via EdgeStream or email
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
| Practice | Reason |
|---|---|
| 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 operations | Queue emails, notifications, and external calls via background jobs — don't block the hook |
| Handle exceptions internally | Don't let exceptions propagate — log and continue; the cycle must not fail due to a notification error |
| Filter by interaction type | Use early returns for types this hook doesn't handle |
Use correlationId to find context | The original business entity (workflow execution ID, entity ID) is in correlationId |