emitBlockedOutbound fires the fire-and-forget email.blocked event for an outbound send refused by screening (applied action = block). messageID is the stable soft-ref (blockAuditID) since no message row is persisted, so the deterministic id keeps retries idempotent. reason_source mirrors the protect
(agent *identity.AgentIdentity, messageID string, req outbound.SendRequest, v outboundVerdict)
| 290 | // keeps retries idempotent. reason_source mirrors the protection_events vocabulary |
| 291 | // (recipient_gate / outbound_scan). |
| 292 | func (a *API) emitBlockedOutbound(agent *identity.AgentIdentity, messageID string, req outbound.SendRequest, v outboundVerdict) { |
| 293 | e := webhookpub.NewEvent(webhookpub.EventEmailBlocked, agent.UserID, map[string]interface{}{ |
| 294 | "message_id": messageID, |
| 295 | "agent": map[string]interface{}{"id": agent.ID, "email": agent.EmailAddress(), "domain": agent.Domain}, |
| 296 | "direction": "outbound", |
| 297 | "recipients": allRecipients(req), |
| 298 | "subject": req.Subject, |
| 299 | "reason": v.Reason, |
| 300 | "reason_source": v.ReviewReason, |
| 301 | }) |
| 302 | e.AgentID = agent.ID |
| 303 | e.ConversationID = req.ConversationID |
| 304 | e.MessageID = messageID |
| 305 | e.ID = webhookpub.DeterministicEventID(messageID, webhookpub.EventEmailBlocked) |
| 306 | a.publishAsync(e) |
| 307 | } |