--- Webhooks-as-a-resource HTTP layer (slice 2) --- The handlers here serve POST/GET/LIST/PATCH/DELETE on /v1/webhooks plus /rotate-secret, /test, /deliveries subresources. The storage layer in internal/identity/webhooks.go does the per-row work; this layer applies the public-facing validation rule
( agent *identity.AgentIdentity, outMsg *identity.Message, result *outbound.SendResult, req outbound.SendRequest, msgType string, )
| 33 | // that case we still publish (the SES send already happened) but with |
| 34 | // an empty message_id; receivers see the event without a row to fetch. |
| 35 | func (a *API) buildSentEvent( |
| 36 | agent *identity.AgentIdentity, |
| 37 | outMsg *identity.Message, |
| 38 | result *outbound.SendResult, |
| 39 | req outbound.SendRequest, |
| 40 | msgType string, |
| 41 | ) webhookpub.Event { |
| 42 | messageID := "" |
| 43 | if outMsg != nil { |
| 44 | messageID = outMsg.ID |
| 45 | } |
| 46 | data := map[string]interface{}{ |
| 47 | "message_id": messageID, |
| 48 | "provider_message_id": result.MessageID, |
| 49 | "method": result.Method, |
| 50 | "from": agent.EmailAddress(), |
| 51 | "to": result.To, |
| 52 | "cc": result.CC, |
| 53 | "bcc": result.BCC, |
| 54 | "subject": req.Subject, |
| 55 | "type": msgType, |
| 56 | "conversation_id": req.ConversationID, |
| 57 | } |
| 58 | return webhookpub.Event{ |
| 59 | ID: generateEventIDForAgent(), |
| 60 | Type: webhookpub.EventEmailSent, |
| 61 | CreatedAt: time.Now().UTC(), |
| 62 | UserID: agent.UserID, |
| 63 | AgentID: agent.ID, |
| 64 | ConversationID: req.ConversationID, |
| 65 | MessageID: messageID, |
| 66 | Data: data, |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | // buildPendingApprovalEvent fires when a HITL-enabled agent holds an |
| 71 | // outbound message for human review. msg is the pending row; req is |