Documentation
¶
Overview ¶
Package eventpayload defines the canonical typed `data` payloads for the STABLE webhook/WS event types (the pre-GA contract freeze, PR-2 of the event/webhook contract audit).
One struct per stable event, used by EVERY builder that emits that event — the relay (email.received), the queue-first outbound path (email.sent / email.failed), the SES delivery-feedback consumer (email.delivered / email.bounced / email.complained, domain.suppression_added), and the sender-identity worker (domain.sending_verified / domain.sending_failed). The WebSocket channel reuses EmailReceivedData verbatim, so webhook and WS payloads for the same event are identical by construction.
The structs are also registered as OpenAPI component schemas (see internal/httpapi's event-payload schema registration), and the committed golden fixtures under testdata/ are the cross-channel drift lock: the server-side builder tests and the TS/Python SDK payload tests all assert against the same fixture bytes.
BETA events (email.flagged, email.blocked, email.review_requested, email.review_approved, email.review_rejected) intentionally stay map[string]any at their trigger sites — their payloads are open/unstable and must NOT be typed here until they are declared stable.
This package stays a light leaf (time + internal/mailparse only) so the stdlib-oriented internal/delivery package can import it without dragging in webhookpub's storage dependencies.
Index ¶
- Variables
- type AgentSuppressionAddedData
- type AttachmentMetaView
- type DomainSendingFailedData
- type DomainSendingVerifiedData
- type DomainSuppressionAddedData
- type EmailBouncedData
- type EmailComplainedData
- type EmailDeliveredData
- type EmailFailedData
- type EmailReceivedData
- type EmailSentData
- type StableEvent
Constants ¶
This section is empty.
Variables ¶
var StableEvents = []StableEvent{ {"email.received", "EmailReceivedData", EmailReceivedData{}, "email.received.json", "email.received.min.json"}, {"email.sent", "EmailSentData", EmailSentData{}, "email.sent.json", "email.sent.min.json"}, {"email.failed", "EmailFailedData", EmailFailedData{}, "email.failed.json", "email.failed.min.json"}, {"email.delivered", "EmailDeliveredData", EmailDeliveredData{}, "email.delivered.json", "email.delivered.min.json"}, {"email.bounced", "EmailBouncedData", EmailBouncedData{}, "email.bounced.json", "email.bounced.min.json"}, {"email.complained", "EmailComplainedData", EmailComplainedData{}, "email.complained.json", "email.complained.min.json"}, {"domain.sending_verified", "DomainSendingVerifiedData", DomainSendingVerifiedData{}, "domain.sending_verified.json", ""}, {"domain.sending_failed", "DomainSendingFailedData", DomainSendingFailedData{}, "domain.sending_failed.json", "domain.sending_failed.min.json"}, {"domain.suppression_added", "DomainSuppressionAddedData", DomainSuppressionAddedData{}, "domain.suppression_added.json", "domain.suppression_added.min.json"}, }
StableEvents is the canonical event-type → payload-schema catalog. Keep the slice ordered for deterministic OpenAPI output and documentation tests.
Functions ¶
This section is empty.
Types ¶
type AgentSuppressionAddedData ¶
type AgentSuppressionAddedData struct {
AgentEmail string `json:"agent_email"`
Address string `json:"address"`
Source string `json:"source" doc:"How the suppression was created. Known values: unsubscribe, manual."`
}
AgentSuppressionAddedData is the exact agent-recipient consent scope added by either the public unsubscribe flow or account-admin management API.
type AttachmentMetaView ¶
type AttachmentMetaView struct {
Filename string `json:"filename,omitempty"`
ContentType string `json:"content_type,omitempty"`
// SizeBytes is the DECODED attachment payload size — the byte count of the
// file after the MIME Content-Transfer-Encoding (base64 / quoted-printable)
// is undone: the size of the file a download yields. NOT the encoded
// on-the-wire size inside the raw MIME, and NOT the message-level
// size_bytes (which is the raw MIME length of the whole message).
SizeBytes int `` /* 185-byte string literal not displayed */
Index int `json:"index" doc:"Stable 0-based attachment index (document order) — the fetch key for the attachment-bytes endpoint."`
// ContentID is the attachment's Content-ID with angle brackets stripped
// (e.g. "ii_abc@mail.gmail.com"), present only for parts that carry one —
// typically inline images embedded by a mail client. It is the key an HTML
// body's `cid:` reference resolves against: a renderer maps
// `<img src="cid:ii_abc@mail.gmail.com">` to the attachment whose
// content_id matches, then fetches the bytes via the attachment endpoint.
// Omitted (empty) for ordinary, non-inline attachments.
ContentID string `` /* 225-byte string literal not displayed */
}
AttachmentMetaView is the CANONICAL wire shape for one attachment's metadata — never the bytes. It is shared by the REST message views (httpapi aliases it), the stable event payloads (EmailReceivedData), and the account export: `index` is the stable 0-based attachment index (document order) used to fetch the bytes via GET /v1/agents/{email}/messages/{id}/attachments/{index}.
func AttachmentMetadata ¶
func AttachmentMetadata(raw []byte) []AttachmentMetaView
AttachmentMetadata extracts the per-attachment metadata of a raw RFC 5322 message for EmailReceivedData.Attachments — the same extraction the REST message views use (mailparse.Attachments), so the event and the resource agree on indexes and sizes. Returns nil when the message has none.
type DomainSendingFailedData ¶
type DomainSendingFailedData struct {
Domain string `json:"domain"`
SendingStatus string `json:"sending_status" doc:"Terminal sending-identity status. Open set; tolerate unknown values. Known values: failed."`
Reason string `json:"reason,omitempty"`
}
DomainSendingFailedData is the `data` payload of a domain.sending_failed event — the domain's async SES sending identity reached a failed terminal state.
type DomainSendingVerifiedData ¶
type DomainSendingVerifiedData struct {
Domain string `json:"domain"`
SendingStatus string `json:"sending_status" doc:"Terminal sending-identity status. Open set; tolerate unknown values. Known values: verified."`
}
DomainSendingVerifiedData is the `data` payload of a domain.sending_verified event — the domain's async SES sending identity reached the verified terminal state.
type DomainSuppressionAddedData ¶
type DomainSuppressionAddedData struct {
Address string `json:"address"`
// Source is an OPEN set (evolving response-side vocabulary, like the REST
// Suppression.source it mirrors — and the DB CHECK in
// migrations/031_delivery_feedback.sql already admits source='manual').
Source string `` /* 185-byte string literal not displayed */
Reason string `json:"reason,omitempty"`
// MessageID is the outbound message whose feedback triggered the
// suppression, when still known.
MessageID string `json:"message_id,omitempty"`
}
DomainSuppressionAddedData is the `data` payload of a domain.suppression_added event — an address was auto-suppressed after a hard bounce or complaint. Account-scoped despite the `domain.` prefix.
type EmailBouncedData ¶
type EmailBouncedData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email"`
Direction string `json:"direction" doc:"Always \"outbound\" on this event."`
DeliveredTo string `json:"delivered_to" doc:"The one recipient address this per-recipient outcome is about."`
Subject string `json:"subject,omitempty"`
SMTPDetail string `json:"smtp_detail,omitempty"`
// BounceType is the normalized SES bounce classification. Only a
// permanent (hard) bounce auto-suppresses the address.
//
// Deliberately a CLOSED enum, unlike the evolving response vocabularies
// (which are open sets): this is a normalized, exhaustive classification —
// normalizeBounceType in internal/delivery/ses.go maps every provider
// value into exactly these three, with `undetermined` as the guaranteed
// catch-all — so the vocabulary cannot grow without a deliberate contract
// change.
BounceType string `json:"bounce_type" enum:"permanent,transient,undetermined"`
// BounceSubType is the raw SES bounceSubType (e.g. General, NoEmail,
// MailboxFull), when present.
BounceSubType string `json:"bounce_sub_type,omitempty"`
}
EmailBouncedData is the `data` payload of an email.bounced event — an outbound message bounced for a recipient. EmailDeliveredData's fields plus the SES bounce classification.
type EmailComplainedData ¶
type EmailComplainedData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email"`
Direction string `json:"direction" doc:"Always \"outbound\" on this event."`
DeliveredTo string `json:"delivered_to" doc:"The one recipient address this per-recipient outcome is about."`
Subject string `json:"subject,omitempty"`
SMTPDetail string `json:"smtp_detail,omitempty"`
}
EmailComplainedData is the `data` payload of an email.complained event — a recipient marked an outbound message as spam (feedback-loop complaint). Same shape as EmailDeliveredData; SMTPDetail carries the complaint feedback type when present.
type EmailDeliveredData ¶
type EmailDeliveredData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email"`
Direction string `json:"direction" doc:"Always \"outbound\" on this event."`
// DeliveredTo is the ONE recipient address this per-recipient outcome is
// about (scalar by construction).
DeliveredTo string `json:"delivered_to" doc:"The one recipient address this per-recipient outcome is about."`
Subject string `json:"subject,omitempty"`
// SMTPDetail is the provider diagnostic string (e.g. the remote SMTP
// response), when the feedback notification carried one.
SMTPDetail string `json:"smtp_detail,omitempty"`
}
EmailDeliveredData is the `data` payload of an email.delivered event — the recipient's server accepted an outbound message, per recipient (one event per (message, recipient)). The event type IS the outcome; there is no redundant `status` field.
type EmailFailedData ¶
type EmailFailedData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email"`
Direction string `json:"direction" doc:"Always \"outbound\" on this event."`
ConversationID string `json:"conversation_id,omitempty"`
Method string `json:"method" doc:"Transport used for the send. Open set; tolerate unknown values. Known values: smtp."`
From string `json:"from"`
To []string `json:"to" nullable:"false"`
CC []string `json:"cc,omitempty" nullable:"false"`
BCC []string `json:"bcc,omitempty" nullable:"false"`
Subject string `json:"subject"`
MessageType string `json:"message_type" doc:"Send kind. Open set; tolerate unknown values. Known values: send, reply, forward."`
// Reason is the human-readable terminal failure diagnostic (e.g. the SMTP
// response of a permanent reject).
Reason string `json:"reason"`
// ReasonCode is an optional machine-readable failure code. Omitted when
// the send path has no classification beyond Reason.
ReasonCode string `json:"reason_code,omitempty"`
// Retryable reports whether re-submitting the same send could succeed.
// Populated only where the send path genuinely knows it; omitted
// otherwise (absent ≠ false).
Retryable *bool `json:"retryable,omitempty"`
}
EmailFailedData is the `data` payload of an email.failed event — an outbound send terminally failed (retries exhausted / permanent reject). Same fields as EmailSentData minus provider_message_id (the provider never accepted it), plus the failure reason.
type EmailReceivedData ¶
type EmailReceivedData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email" doc:"The receiving agent's email — its id and address (an agent's id IS its email)."`
Direction string `json:"direction" doc:"Always \"inbound\" on this event."`
// ConversationID is empty for a thread-starting message.
ConversationID string `json:"conversation_id,omitempty"`
HeaderFrom *string `json:"header_from" nullable:"true" doc:"Parsed RFC 5322 From address; never replaced by Reply-To."`
EnvelopeFrom *string `` /* 147-byte string literal not displayed */
VerifiedDomain *string `` /* 471-byte string literal not displayed */
To []string `json:"to" nullable:"false"`
CC []string `json:"cc" nullable:"false"`
ReplyTo []string `json:"reply_to" nullable:"false"`
Authentication *emailauth.Authentication `` /* 326-byte string literal not displayed */
// DeliveredTo is the agent address this copy was delivered to — a SCALAR
// by construction: the relay emits one event per per-agent delivery, so
// unlike the peer To/CC lists (the message's parsed headers) this is
// always exactly one address. It is the fetch key for the message.
DeliveredTo string `` /* 164-byte string literal not displayed */
Subject string `json:"subject"`
ReceivedAt time.Time `json:"received_at" format:"date-time"`
// Attachments is per-attachment METADATA (never bytes) parsed from the
// raw message. Omitted when the message has none.
Attachments []AttachmentMetaView `json:"attachments,omitempty" nullable:"false"`
}
EmailReceivedData is the `data` payload of an email.received event. The event is a metadata-only NOTIFICATION, never a content carrier: fetch the full message (body + attachment bytes) from GET /v1/agents/{delivered_to}/messages/{message_id}.
type EmailSentData ¶
type EmailSentData struct {
MessageID string `json:"message_id"`
AgentEmail string `json:"agent_email"`
Direction string `json:"direction" doc:"Always \"outbound\" on this event."`
ConversationID string `json:"conversation_id,omitempty"`
// ProviderMessageID is the provider-assigned (SES) message id — distinct
// from the e2a message_id, and the correlation key for the async
// delivered/bounced/complained feedback events. Omitted for providerless
// local loopback delivery.
ProviderMessageID string `json:"provider_message_id,omitempty"`
Method string `json:"method" doc:"Transport used for the send. Open set; tolerate unknown values. Known values: smtp, loopback."`
From string `json:"from"`
To []string `json:"to" nullable:"false"`
CC []string `json:"cc,omitempty" nullable:"false"`
BCC []string `json:"bcc,omitempty" nullable:"false"`
Subject string `json:"subject"`
MessageType string `json:"message_type" doc:"Send kind. Open set; tolerate unknown values. Known values: send, reply, forward."`
}
EmailSentData is the `data` payload of an email.sent event — an outbound send reached its terminal sent state, either by provider acceptance or by atomic local loopback delivery.
type StableEvent ¶
type StableEvent struct {
Type string
SchemaName string
Payload any
Fixture string
MinimalFixture string
}
StableEvent describes one GA-frozen event payload contract. It is the source consumed by OpenAPI registration and fixture coverage; the event publisher's complete vocabulary additionally contains explicitly experimental event types.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package goldenassert is the shared TEST helper for the golden-payload fixtures under internal/eventpayload/testdata — the cross-channel drift lock for the stable event `data` payloads.
|
Package goldenassert is the shared TEST helper for the golden-payload fixtures under internal/eventpayload/testdata — the cross-channel drift lock for the stable event `data` payloads. |