Documentation
¶
Index ¶
- Constants
- func InitialMailStatus(kind string) string
- func ParseSender(fromField, nameFallback string) (name, email string, err error)
- func ReplacePlaceholders(template string, vars map[string]any) string
- func ResolveMailLogStatusTransition(current, incoming string) (next string, apply bool)
- func SendCloudEventToStatus(event string) string
- func SendCloudStatusToMailStatus(status string) string
- func VerifySendCloudInboundSignature(event *SendCloudInboundEvent, apiKey string) bool
- type Channel
- type IMAPConfig
- type IMAPReader
- type MailAttachment
- type MailLog
- type MailLogStore
- type MailMessage
- type MailProvider
- type MailReader
- type Mailer
- type MailerOption
- type MemoryMailLogStore
- func (s *MemoryMailLogStore) CreateFailedMailLog(log *MailLog) error
- func (s *MemoryMailLogStore) CreateMailLog(log *MailLog) error
- func (s *MemoryMailLogStore) GetMailLogByMessageID(messageID string) (*MailLog, error)
- func (s *MemoryMailLogStore) GetMailLogStats() (map[string]int64, error)
- func (s *MemoryMailLogStore) GetMailLogs(page, pageSize int) ([]*MailLog, int64, error)
- func (s *MemoryMailLogStore) UpdateMailLogStatusByMessageID(messageID, provider, status, errorMsg string) error
- type MemoryMailReader
- type RetryPolicy
- type SMTPConfig
- type SMTPProvider
- type SendCloudConfig
- type SendCloudDeliveryRecord
- type SendCloudInboundEvent
- type SendCloudProvider
- type SendCloudReader
- func (r *SendCloudReader) AddInboundRoute(expression, action, apiUserRoute string) (int, error)
- func (r *SendCloudReader) Close() error
- func (r *SendCloudReader) DeleteInboundRoute(routeID int) error
- func (r *SendCloudReader) DeleteMessage(id string) error
- func (r *SendCloudReader) Kind() string
- func (r *SendCloudReader) ListInboundRoutes(domain string, start, limit int) ([]SendCloudRoute, error)
- func (r *SendCloudReader) MarkRead(id string) error
- func (r *SendCloudReader) QueryStatus(q SendCloudStatusQuery) ([]SendCloudDeliveryRecord, int, error)
- func (r *SendCloudReader) ReadMessages(limit int) ([]*MailMessage, error)
- type SendCloudRoute
- type SendCloudStatusQuery
- type SendCloudWebhookEvent
Constants ¶
const ( StatusSpam = "spam" StatusClicked = "clicked" StatusOpened = "opened" StatusUnsubscribed = "unsubscribed" StatusUnknown = "unknown" )
Additional delivery status constants for SendCloud webhook events.
const ( StatusSent = "sent" StatusFailed = "failed" StatusPending = "pending" StatusDelivered = "delivered" StatusSoftBounce = "soft_bounce" StatusInvalid = "invalid" )
Delivery status constants. These mirror the values used by the parent notification package's LogEntry.Status field.
Variables ¶
This section is empty.
Functions ¶
func InitialMailStatus ¶
InitialMailStatus returns the DB status right after a successful provider send. SMTP starts as "delivered" (synchronous handoff); SendCloud starts as "sent" (async, refined by webhooks).
func ParseSender ¶
ParseSender parses a "From" header value of the form "Name <email@x.com>" or a bare "email@x.com". When no name is present, nameFallback is returned as the display name. The returned email is validated; an error is returned for malformed input.
func ReplacePlaceholders ¶
ReplacePlaceholders substitutes every "{{.Key}}" occurrence in template with the stringified value of vars[Key]. Missing keys are left untouched (the placeholder text is preserved).
func ResolveMailLogStatusTransition ¶
ResolveMailLogStatusTransition decides whether to apply an incoming status to a mail log row. It returns the next status and whether the update should be applied.
Rules:
- Empty incoming status: no change.
- Same status: no change.
- Terminal failure incoming: always apply (overrides any success).
- Terminal failure current: do not downgrade.
- Higher rank incoming: apply.
- Lower or equal rank incoming: no change.
func SendCloudEventToStatus ¶
SendCloudEventToStatus maps SendCloud webhook event codes to mail status.
func SendCloudStatusToMailStatus ¶
SendCloudStatusToMailStatus maps a SendCloud delivery status string (e.g. "送达", "无效邮件-地址不存在", "软退信-服务不可达") to a normalized mail log status.
func VerifySendCloudInboundSignature ¶
func VerifySendCloudInboundSignature(event *SendCloudInboundEvent, apiKey string) bool
VerifySendCloudInboundSignature verifies the signature of an inbound webhook event. SendCloud computes the signature as MD5(token + api_key) and sends it in the `signature` field. Pass your API key to verify.
Types ¶
type Channel ¶
type Channel struct {
// contains filtered or unexported fields
}
Channel adapts a Mailer to the notification.Channel interface so it can be registered with a Dispatcher.
func NewChannel ¶
NewChannel creates an email Channel backed by the given Mailer. The channel is enabled by default.
func (*Channel) Send ¶
Send delivers an email Message via the underlying Mailer. HTML bodies are preferred when present.
func (*Channel) SetEnabled ¶
SetEnabled toggles the channel on or off.
func (*Channel) Type ¶
func (c *Channel) Type() notification.MessageType
Type returns notification.TypeEmail.
type IMAPConfig ¶
type IMAPConfig struct {
Host string // IMAP server host (e.g. "imap.qq.com")
Port int // IMAP server port (typically 993 for TLS)
Username string // account username (full email address)
Password string // account password or authorization code
Mailbox string // mailbox name (default "INBOX")
}
IMAPConfig holds the connection settings for an IMAP server.
type IMAPReader ¶
type IMAPReader struct {
// contains filtered or unexported fields
}
IMAPReader implements MailReader using the IMAP protocol. It connects via TLS, logs in, and fetches messages from the configured mailbox.
func NewIMAPReader ¶
func NewIMAPReader(cfg IMAPConfig) (*IMAPReader, error)
NewIMAPReader creates a new IMAPReader and establishes a connection to the IMAP server. The connection is ready for ReadMessages calls after construction. Call Close to release the connection.
func (*IMAPReader) Close ¶
func (r *IMAPReader) Close() error
Close logs out and closes the IMAP connection.
func (*IMAPReader) DeleteMessage ¶
func (r *IMAPReader) DeleteMessage(id string) error
DeleteMessage marks a message as deleted and expunges it. The message ID should be the UID.
func (*IMAPReader) MarkRead ¶
func (r *IMAPReader) MarkRead(id string) error
MarkRead marks a message as read by adding the \Seen flag. The message ID should be the UID from the fetched message.
func (*IMAPReader) ReadMessages ¶
func (r *IMAPReader) ReadMessages(limit int) ([]*MailMessage, error)
ReadMessages fetches up to limit unread (unseen) messages from the mailbox. If limit <= 0, all unread messages are fetched. Messages are returned newest-first.
func (*IMAPReader) ReadRecentMessages ¶
func (r *IMAPReader) ReadRecentMessages(limit int) ([]*MailMessage, error)
ReadRecentMessages fetches up to limit most recent messages regardless of read state. If limit <= 0, all messages are fetched. Messages are returned newest-first.
type MailAttachment ¶
MailAttachment is a parsed email attachment.
type MailLog ¶
type MailLog struct {
ID string // unique identifier (provider message ID or generated)
Provider string // provider kind: "smtp", "sendcloud", etc.
ChannelName string // channel label for multi-channel setups
ToEmail string // recipient address
Subject string // email subject
HtmlBody string // email HTML body
Status string // delivery status (see status constants)
ErrorMsg string // error message on failure
MessageID string // provider-assigned message ID
RetryCount int // number of retry attempts
SentAt time.Time // when the send was initiated
CreatedAt time.Time // when the log was created
UpdatedAt time.Time // when the log was last updated
}
MailLog is a persisted record of an outbound email send attempt. It tracks the delivery lifecycle from initial send through webhook status updates (delivered, opened, bounced, etc.).
type MailLogStore ¶
type MailLogStore interface {
// CreateMailLog records a successful or accepted send.
CreateMailLog(log *MailLog) error
// CreateFailedMailLog records a send that failed after all retries.
CreateFailedMailLog(log *MailLog) error
// UpdateMailLogStatusByMessageID updates the status of a log entry
// identified by its provider message ID. Status transitions follow
// lifecycle ordering so late webhooks cannot downgrade a more
// advanced status.
UpdateMailLogStatusByMessageID(messageID, provider, status, errorMsg string) error
// GetMailLogByMessageID returns a log entry by provider message ID.
GetMailLogByMessageID(messageID string) (*MailLog, error)
// GetMailLogs returns paginated logs, most recent first.
GetMailLogs(page, pageSize int) ([]*MailLog, int64, error)
// GetMailLogStats returns status counts.
GetMailLogStats() (map[string]int64, error)
}
MailLogStore is the persistence abstraction for mail logs. Implementations may use an in-memory map, a database, or any other storage backend.
type MailMessage ¶
type MailMessage struct {
ID string // message ID from headers
From string // sender address
FromName string // sender display name
To []string // recipient addresses
Cc []string // CC addresses
Subject string // subject line
TextBody string // plain text body
HTMLBody string // HTML body
ReplyTo string // Reply-To header
Date time.Time // Date header
Headers mail.Header // raw headers
Attachments []MailAttachment
}
MailMessage is a parsed inbound email message.
func ParseMailMessage ¶
func ParseMailMessage(raw []byte) (*MailMessage, error)
ParseMailMessage parses a raw RFC 822 message (as returned by IMAP FETCH or POP3 RETR) into a MailMessage. It extracts text and HTML bodies, handles multipart/alternative and multipart/mixed, and collects attachments.
type MailProvider ¶
type MailProvider interface {
// Kind returns a short identifier for the provider (e.g. "smtp").
Kind() string
// SendHTMLWith sends an HTML message, applying the given template
// variables to the subject and body before delivery. It returns the
// provider-assigned message ID (if any) and an error.
SendHTMLWith(to, subject, htmlBody string, vars map[string]any) (string, error)
// SendTextWith sends a plain-text message, applying the given
// template variables to the subject and body before delivery. It
// returns the provider-assigned message ID (if any) and an error.
SendTextWith(to, subject, textBody string, vars map[string]any) (string, error)
}
MailProvider is the interface implemented by email backends (SMTP, third-party HTTP APIs, etc.). Each provider knows how to render and deliver a single message.
type MailReader ¶
type MailReader interface {
// ReadMessages fetches up to limit unread messages from the mailbox.
// If limit <= 0, all unread messages are fetched.
ReadMessages(limit int) ([]*MailMessage, error)
// MarkRead marks the message with the given ID as read.
MarkRead(id string) error
// DeleteMessage deletes the message with the given ID.
DeleteMessage(id string) error
// Close closes the reader and releases any resources.
Close() error
}
MailReader is the abstraction for fetching and reading emails from a mailbox (IMAP, POP3, API-based, etc.).
type Mailer ¶
type Mailer struct {
// contains filtered or unexported fields
}
Mailer is a multi-channel email sender with per-provider retry and cross-provider failover. Providers are selected round-robin starting at startingIndex, which advances after every successful send.
func NewMailer ¶
func NewMailer(providers []MailProvider, opts ...MailerOption) *Mailer
NewMailer creates a Mailer over the given providers. At least one provider is required; options may override the retry policy and starting index.
func (*Mailer) Send ¶
Send delivers an HTML message. It tries providers in round-robin order; each provider is retried up to RetryPolicy.MaxAttempts with exponential backoff before failing over to the next provider.
func (*Mailer) SendText ¶
SendText delivers a plain-text message using the same retry/failover strategy as Send.
func (*Mailer) SendWithTemplate ¶
func (m *Mailer) SendWithTemplate(ctx context.Context, to, subject, templateCode string, vars map[string]any, templateStore notification.TemplateStore) error
SendWithTemplate loads a template from the store, renders it with vars, and sends the result as HTML (falling back to text when the template body is not HTML).
type MailerOption ¶
type MailerOption func(*Mailer)
MailerOption configures a Mailer at construction time.
func WithRetryPolicy ¶
func WithRetryPolicy(p RetryPolicy) MailerOption
WithRetryPolicy overrides the default retry policy.
func WithStartingIndex ¶
func WithStartingIndex(i int) MailerOption
WithStartingIndex sets the initial round-robin provider index.
type MemoryMailLogStore ¶
type MemoryMailLogStore struct {
// contains filtered or unexported fields
}
MemoryMailLogStore is a thread-safe in-memory MailLogStore. It is primarily intended for testing and small-scale deployments.
func NewMemoryMailLogStore ¶
func NewMemoryMailLogStore() *MemoryMailLogStore
NewMemoryMailLogStore creates a new empty MemoryMailLogStore.
func (*MemoryMailLogStore) CreateFailedMailLog ¶
func (s *MemoryMailLogStore) CreateFailedMailLog(log *MailLog) error
CreateFailedMailLog records a send that failed after all retries.
func (*MemoryMailLogStore) CreateMailLog ¶
func (s *MemoryMailLogStore) CreateMailLog(log *MailLog) error
CreateMailLog records a successful or accepted send.
func (*MemoryMailLogStore) GetMailLogByMessageID ¶
func (s *MemoryMailLogStore) GetMailLogByMessageID(messageID string) (*MailLog, error)
GetMailLogByMessageID returns a log entry by provider message ID.
func (*MemoryMailLogStore) GetMailLogStats ¶
func (s *MemoryMailLogStore) GetMailLogStats() (map[string]int64, error)
GetMailLogStats returns status counts.
func (*MemoryMailLogStore) GetMailLogs ¶
func (s *MemoryMailLogStore) GetMailLogs(page, pageSize int) ([]*MailLog, int64, error)
GetMailLogs returns paginated logs, most recent first.
func (*MemoryMailLogStore) UpdateMailLogStatusByMessageID ¶
func (s *MemoryMailLogStore) UpdateMailLogStatusByMessageID(messageID, provider, status, errorMsg string) error
UpdateMailLogStatusByMessageID updates the status of a log entry.
type MemoryMailReader ¶
type MemoryMailReader struct {
// contains filtered or unexported fields
}
MemoryMailReader is a simple in-memory MailReader for testing. It stores messages as raw bytes and parses them on demand.
func NewMemoryMailReader ¶
func NewMemoryMailReader() *MemoryMailReader
NewMemoryMailReader creates a new empty MemoryMailReader.
func (*MemoryMailReader) AddMessage ¶
func (r *MemoryMailReader) AddMessage(id string, raw []byte)
AddMessage adds a raw RFC 822 message to the reader.
func (*MemoryMailReader) Close ¶
func (r *MemoryMailReader) Close() error
Close is a no-op for the in-memory reader.
func (*MemoryMailReader) DeleteMessage ¶
func (r *MemoryMailReader) DeleteMessage(id string) error
DeleteMessage removes a message.
func (*MemoryMailReader) MarkRead ¶
func (r *MemoryMailReader) MarkRead(id string) error
MarkRead marks a message as read.
func (*MemoryMailReader) ReadMessages ¶
func (r *MemoryMailReader) ReadMessages(limit int) ([]*MailMessage, error)
ReadMessages fetches up to limit unread messages.
type RetryPolicy ¶
type RetryPolicy struct {
MaxAttempts int // total attempts per provider (>=1)
InitialBackoff time.Duration // backoff before the first retry
MaxBackoff time.Duration // upper bound for backoff growth
}
RetryPolicy controls retry behaviour for the Mailer.
func DefaultRetryPolicy ¶
func DefaultRetryPolicy() RetryPolicy
DefaultRetryPolicy returns a sensible default retry policy: 3 attempts, starting at 1s, capped at 10s.
type SMTPConfig ¶
type SMTPConfig struct {
Host string // SMTP server host (e.g. "smtp.example.com")
Port int // SMTP server port (e.g. 25, 465, 587)
Username string // authentication username (optional)
Password string // authentication password (optional)
From string // sender address, may be "Name <addr@example.com>"
FromName string // fallback sender display name when From has no name
}
SMTPConfig holds the connection settings for an SMTP server.
type SMTPProvider ¶
type SMTPProvider struct {
// contains filtered or unexported fields
}
SMTPProvider sends mail via a plain SMTP server using net/smtp.
func NewSMTPProvider ¶
func NewSMTPProvider(cfg SMTPConfig) *SMTPProvider
NewSMTPProvider constructs an SMTPProvider from the given config.
func (*SMTPProvider) SendHTMLWith ¶
func (p *SMTPProvider) SendHTMLWith(to, subject, htmlBody string, vars map[string]any) (string, error)
SendHTMLWith renders the subject and HTML body with vars and sends the message as text/html.
func (*SMTPProvider) SendTextWith ¶
func (p *SMTPProvider) SendTextWith(to, subject, textBody string, vars map[string]any) (string, error)
SendTextWith renders the subject and text body with vars and sends the message as text/plain.
type SendCloudConfig ¶
type SendCloudConfig struct {
APIUser string // SendCloud API user
APIKey string // SendCloud API key
From string // sender address (may be "Name <email@x.com>")
FromName string // fallback sender display name
Endpoint string // API endpoint override (defaults to the public endpoint)
}
SendCloudConfig holds SendCloud API credentials and sender defaults.
type SendCloudDeliveryRecord ¶
type SendCloudDeliveryRecord struct {
EmailID string `json:"emailId"`
Status string `json:"status"`
SubStatus string `json:"subStatus"`
SubStatusDesc string `json:"subStatusDesc"`
APIUser string `json:"apiUser"`
Recipients string `json:"recipients"`
RequestTime string `json:"requestTime"`
ModifiedTime string `json:"modifiedTime"`
SendLog string `json:"sendLog"`
}
SendCloudDeliveryRecord is a single delivery-status entry returned by the SendCloud emailStatus API.
func (SendCloudDeliveryRecord) ParsedModifiedTime ¶
func (r SendCloudDeliveryRecord) ParsedModifiedTime() time.Time
ParsedModifiedTime returns the parsed modified time, or zero on error.
func (SendCloudDeliveryRecord) ParsedRequestTime ¶
func (r SendCloudDeliveryRecord) ParsedRequestTime() time.Time
ParsedRequestTime returns the parsed request time, or zero on error.
type SendCloudInboundEvent ¶
type SendCloudInboundEvent struct {
Event string `json:"event"` // "route"
Message string `json:"message"` // "mx route"
Timestamp int64 `json:"timestamp"` // unix timestamp
From string `json:"from"` // header From address
FromName string `json:"fromname"` // From display name
To string `json:"to"` // header To address
ToName string `json:"toname"` // To display name
XMXMailFrom string `json:"x_mx_mailfrom"` // envelope sender
XMXRcptTo string `json:"x_mx_rcptto"` // envelope recipient
Headers string `json:"headers"` // raw headers (JSON)
HTML string `json:"html"` // html body
Text string `json:"text"` // text body
Subject string `json:"subject"` // subject
RawMessageURL string `json:"raw_message_url"` // .eml download URL (15-day TTL)
RawMessage string `json:"raw_message"` // raw RFC 822 message
Token string `json:"token"` // random 50-char string
Signature string `json:"signature"` // signature string
UserHeaders string `json:"userHeaders"` // custom SC-Custom-* headers
Reference string `json:"reference"` // original SendCloud Message-ID
EmailID string `json:"emailId"` // parent email ID
LabelID int `json:"labelId"` // parent label ID
LabelName string `json:"labelName"` // parent label name
}
SendCloudInboundEvent is a parsed inbound (route) webhook payload from SendCloud, representing a reply email forwarded by a 收信路由.
func ParseSendCloudInboundEvent ¶
func ParseSendCloudInboundEvent(data []byte) (*SendCloudInboundEvent, error)
ParseSendCloudInboundEvent parses a JSON or x-www-form-urlencoded SendCloud route webhook body into an inbound event.
func (*SendCloudInboundEvent) ToMailMessage ¶
func (e *SendCloudInboundEvent) ToMailMessage() (*MailMessage, error)
ToMailMessage converts an inbound event into a MailMessage. If RawMessage is present it is parsed for full body/attachment extraction; otherwise the HTML/Text fields are used directly.
type SendCloudProvider ¶
type SendCloudProvider struct {
// contains filtered or unexported fields
}
SendCloudProvider sends email via the SendCloud HTTP API.
func NewSendCloudProvider ¶
func NewSendCloudProvider(cfg SendCloudConfig) (*SendCloudProvider, error)
NewSendCloudProvider constructs a SendCloudProvider from the given config.
func (*SendCloudProvider) Kind ¶
func (p *SendCloudProvider) Kind() string
Kind returns "sendcloud".
func (*SendCloudProvider) SendHTMLWith ¶
func (p *SendCloudProvider) SendHTMLWith(to, subject, htmlBody string, vars map[string]any) (string, error)
SendHTMLWith renders the subject and HTML body with vars and sends the message via the SendCloud API.
func (*SendCloudProvider) SendTextWith ¶
func (p *SendCloudProvider) SendTextWith(to, subject, textBody string, vars map[string]any) (string, error)
SendTextWith renders the subject and text body with vars and sends the message via the SendCloud API.
type SendCloudReader ¶
type SendCloudReader struct {
// contains filtered or unexported fields
}
SendCloudReader queries the SendCloud emailStatus API for delivery records of previously-sent messages. It implements MailReader by mapping delivery records onto MailMessage structs (body is not available — only status metadata).
func NewSendCloudReader ¶
func NewSendCloudReader(cfg SendCloudConfig) (*SendCloudReader, error)
NewSendCloudReader constructs a SendCloudReader from the given config. The API user/key must be set; From is not required for status queries.
func (*SendCloudReader) AddInboundRoute ¶
func (r *SendCloudReader) AddInboundRoute(expression, action, apiUserRoute string) (int, error)
AddInboundRoute creates a new inbound route. expression is the matching pattern, e.g. "contact@email.lingecho.com" or ".*@email.lingecho.com" (regex@domain). action is the destination — either a webhook URL (e.g. "https://yourapp.com/webhook/sendcloud") or an email address to forward to (e.g. "you@qq.com"). When action is an email address, apiUserRoute must be set to the API_USER used for forwarding.
func (*SendCloudReader) Close ¶
func (r *SendCloudReader) Close() error
Close releases the HTTP client. No persistent connection to maintain.
func (*SendCloudReader) DeleteInboundRoute ¶
func (r *SendCloudReader) DeleteInboundRoute(routeID int) error
DeleteInboundRoute removes an inbound route by ID.
func (*SendCloudReader) DeleteMessage ¶
func (r *SendCloudReader) DeleteMessage(id string) error
DeleteMessage is a no-op for SendCloud (delivery records cannot be deleted via this API).
func (*SendCloudReader) ListInboundRoutes ¶
func (r *SendCloudReader) ListInboundRoutes(domain string, start, limit int) ([]SendCloudRoute, error)
ListInboundRoutes queries existing inbound routes.
func (*SendCloudReader) MarkRead ¶
func (r *SendCloudReader) MarkRead(id string) error
MarkRead is a no-op for SendCloud (delivery records are read-only).
func (*SendCloudReader) QueryStatus ¶
func (r *SendCloudReader) QueryStatus(q SendCloudStatusQuery) ([]SendCloudDeliveryRecord, int, error)
QueryStatus fetches delivery-status records matching the given query. Returns the records plus the total count reported by the API.
func (*SendCloudReader) ReadMessages ¶
func (r *SendCloudReader) ReadMessages(limit int) ([]*MailMessage, error)
ReadMessages fetches up to `limit` recent delivery records and maps them onto MailMessage. The From field is set to the configured sender.
type SendCloudRoute ¶
type SendCloudRoute struct {
ID int `json:"id"`
Domain string `json:"domain"`
Expression string `json:"expression"` // e.g. "reply@yourdomain.com" or ".*@yourdomain.com"
Action string `json:"action"` // "URL" or "邮箱"
APIUserRoute string `json:"apiUserRoute"` // required when action is email
}
SendCloudRoute is an inbound route configuration entry.
type SendCloudStatusQuery ¶
type SendCloudStatusQuery struct {
Email string // filter by recipient address
EmailIDs []string // filter by SendCloud email IDs (joined with ';')
LabelID string
LabelName string
APIUserList []string // multiple apiUser filter (joined with ';')
Days int // shortcut for "past N days" (1 = today); max 3
StartDate string // yyyy-MM-dd; required if Days == 0
EndDate string // yyyy-MM-dd; required if Days == 0
Start int // offset, default 0
Limit int // 0-100, default 100
Status string // "1" delivered, "4" invalid, "5" soft-bounce, "18" requested
SubStatus string // e.g. "401;406"
}
SendCloudStatusQuery holds the filters for a SendCloud emailStatus query. Either Days or (StartDate + EndDate) must be set; the query window cannot exceed 3 days.
type SendCloudWebhookEvent ¶
type SendCloudWebhookEvent struct {
Event string `json:"event"`
MessageID string `json:"messageId"`
Email string `json:"email"`
Timestamp int64 `json:"timestamp"`
SmtpStatus string `json:"smtpStatus"`
SmtpError string `json:"smtpError"`
}
SendCloudWebhookEvent is a normalized webhook payload from SendCloud. It can be received as JSON or x-www-form-urlencoded.
func ParseSendCloudWebhookEvent ¶
func ParseSendCloudWebhookEvent(data []byte) (*SendCloudWebhookEvent, error)
ParseSendCloudWebhookEvent parses a JSON or x-www-form-urlencoded SendCloud webhook body into a normalized event.