eventpayload

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL