Documentation
¶
Overview ¶
Package primitive provides webhook verification and validation helpers for Primitive.
The package is centered around HandleWebhookEvent and HandleWebhook, which verify the Primitive-Signature header, decode the raw request body, and parse the JSON payload.
HandleWebhookEvent preserves unknown future event types as UnknownEvent values. HandleWebhook is the email.received-specific convenience wrapper.
Import the module path github.com/primitivedotdev/sdks/sdk-go and use the package name primitive in code.
For lower-level use cases, applications can call VerifyWebhookSignature, ParseWebhookEvent, or ValidateEmailReceivedEvent directly.
Unknown future event types are preserved as UnknownEvent values so consumers can continue receiving webhook traffic before a package update ships.
Index ¶
- Constants
- Variables
- func ConfirmedHeaders() map[string]string
- func DecodeRawEmail(event any, verify ...bool) ([]byte, error)
- func GetDownloadTimeRemaining(event any, nowMillis ...int64) (int64, error)
- func IsDownloadExpired(event any, nowMillis ...int64) (bool, error)
- func IsEmailReceivedEvent(event any) bool
- func IsRawIncluded(event any) (bool, error)
- func ParseJSONBody(rawBody any) (any, error)
- func PrepareStandardWebhooksSecret(secret any) ([]byte, error)
- func VerifyRawEmailDownload(downloaded []byte, event any) ([]byte, error)
- func VerifyStandardWebhooksSignature(options StandardWebhooksVerifyOptions) (bool, error)
- func VerifyWebhookSignature(options VerifyOptions) (bool, error)
- type AuthConfidence
- type AuthVerdict
- type DKIMSignature
- type Delivery
- type DkimResult
- type DmarcPolicy
- type DmarcResult
- type DownloadInfo
- type Email
- type EmailAddress
- type EmailAnalysis
- type EmailAuth
- type EmailContent
- type EmailHeaders
- type EmailReceivedEvent
- type ErrorDefinition
- type EventType
- type ForwardAnalysis
- type ForwardOriginalSender
- type ForwardResult
- type ForwardVerdict
- type ForwardVerification
- type HandleWebhookOptions
- type ParsedData
- type ParsedError
- type ParsedStatus
- type PrimitiveWebhookError
- func (e *PrimitiveWebhookError) As(target any) bool
- func (e *PrimitiveWebhookError) Code() string
- func (e *PrimitiveWebhookError) Error() string
- func (e *PrimitiveWebhookError) Message() string
- func (e *PrimitiveWebhookError) Name() string
- func (e *PrimitiveWebhookError) Suggestion() string
- func (e *PrimitiveWebhookError) ToMap() map[string]any
- func (e *PrimitiveWebhookError) Unwrap() error
- type RawContent
- type RawEmailDecodeError
- type SMTPEnvelope
- type SignResult
- type SpamAssassinAnalysis
- type SpfResult
- type StandardWebhooksSignResult
- type StandardWebhooksVerifyOptions
- type UnknownEvent
- type ValidateEmailAuthResult
- type ValidationIssue
- type ValidationResult
- type VerifyOptions
- type WebhookAttachment
- type WebhookEvent
- type WebhookPayloadError
- type WebhookValidationError
- type WebhookVerificationError
Constants ¶
const ( WebhookVersion = "2025-12-14" PrimitiveSignatureHeader = "Primitive-Signature" LegacySignatureHeader = "MyMX-Signature" PrimitiveConfirmedHeader = "X-Primitive-Confirmed" LegacyConfirmedHeader = "X-MyMX-Confirmed" StandardWebhookIDHeader = "webhook-id" StandardWebhookTimestampHeader = "webhook-timestamp" StandardWebhookSignatureHeader = "webhook-signature" )
Variables ¶
var EmailReceivedEventJSONSchema map[string]any
var PayloadErrors = map[string]ErrorDefinition{
"PAYLOAD_NULL": {
Message: "Webhook payload is null",
Suggestion: "Ensure you're passing the parsed JSON body, not null. Check your framework's body parsing middleware.",
},
"PAYLOAD_UNDEFINED": {
Message: "Webhook payload is undefined",
Suggestion: "The payload was not provided. Make sure you're passing the request body to the handler.",
},
"PAYLOAD_WRONG_TYPE": {
Message: "Webhook payload must be an object",
Suggestion: "The payload should be a parsed JSON object. Check that you're not passing a string or other primitive.",
},
"PAYLOAD_IS_ARRAY": {
Message: "Webhook payload is an array, expected object",
Suggestion: "Primitive webhooks are single event objects, not arrays. Check the payload structure.",
},
"PAYLOAD_MISSING_EVENT": {
Message: "Webhook payload missing 'event' field",
Suggestion: "All webhook payloads must have an 'event' field. This may not be a valid Primitive webhook.",
},
"PAYLOAD_UNKNOWN_EVENT": {
Message: "Unknown webhook event type",
Suggestion: "This event type is not recognized. You may need to update your SDK or handle unknown events gracefully.",
},
"PAYLOAD_EMPTY_BODY": {
Message: "Request body is empty",
Suggestion: "The request body was empty. Ensure the webhook is sending data and your framework is parsing it correctly.",
},
"JSON_PARSE_FAILED": {
Message: "Failed to parse JSON body",
Suggestion: "The request body is not valid JSON. Check the raw body content and Content-Type header.",
},
"INVALID_ENCODING": {
Message: "Invalid body encoding",
Suggestion: "The request body encoding is not supported. Primitive webhooks use UTF-8 encoded JSON.",
},
}
var RawEmailErrors = map[string]ErrorDefinition{
"NOT_INCLUDED": {
Message: "Raw email content not included inline",
Suggestion: "Use the download URL at event.email.content.download.url to fetch the raw email.",
},
"INVALID_BASE64": {
Message: "Raw email content is not valid base64",
Suggestion: "The raw email data is malformed. Fetch the raw email from the download URL or regenerate the webhook payload.",
},
"HASH_MISMATCH": {
Message: "SHA-256 hash verification failed",
Suggestion: "The raw email data may be corrupted. Try downloading from the URL instead.",
},
}
var VerificationErrors = map[string]ErrorDefinition{
"INVALID_SIGNATURE_HEADER": {
Message: "Missing or malformed Primitive-Signature header",
Suggestion: "Check that you're reading the correct header (Primitive-Signature) and it's being passed correctly from your web framework.",
},
"TIMESTAMP_OUT_OF_RANGE": {
Message: "Timestamp is too old (possible replay attack)",
Suggestion: "This could indicate a replay attack, network delay, or server clock drift. Check your server's time is synced.",
},
"SIGNATURE_MISMATCH": {
Message: "Signature doesn't match expected value",
Suggestion: "Verify the webhook secret matches and you're using the raw request body (not re-serialized JSON).",
},
"MISSING_SECRET": {
Message: "No webhook secret was provided",
Suggestion: "Pass your webhook secret from the Primitive dashboard. Check that the environment variable is set.",
},
}
Functions ¶
func ConfirmedHeaders ¶
func IsEmailReceivedEvent ¶
func IsRawIncluded ¶
func ParseJSONBody ¶
func PrepareStandardWebhooksSecret ¶
PrepareStandardWebhooksSecret strips the "whsec_" prefix if present, then base64-decodes the remainder to produce the raw HMAC key bytes.
func VerifyRawEmailDownload ¶
func VerifyStandardWebhooksSignature ¶
func VerifyStandardWebhooksSignature(options StandardWebhooksVerifyOptions) (bool, error)
VerifyStandardWebhooksSignature verifies a Standard Webhooks signature.
func VerifyWebhookSignature ¶
func VerifyWebhookSignature(options VerifyOptions) (bool, error)
Types ¶
type AuthConfidence ¶
type AuthConfidence string
const ( AuthConfidenceHigh AuthConfidence = "high" AuthConfidenceMedium AuthConfidence = "medium" AuthConfidenceLow AuthConfidence = "low" )
type AuthVerdict ¶
type AuthVerdict string
const ( AuthVerdictLegit AuthVerdict = "legit" AuthVerdictSuspicious AuthVerdict = "suspicious" AuthVerdictUnknown AuthVerdict = "unknown" )
type DKIMSignature ¶
type DkimResult ¶
type DkimResult string
const ( DkimResultPass DkimResult = "pass" DkimResultFail DkimResult = "fail" DkimResultTemperror DkimResult = "temperror" DkimResultPermerror DkimResult = "permerror" )
type DmarcPolicy ¶
type DmarcPolicy string
const ( DmarcPolicyReject DmarcPolicy = "reject" DmarcPolicyQuarantine DmarcPolicy = "quarantine" DmarcPolicyNone DmarcPolicy = "none" )
type DmarcResult ¶
type DmarcResult string
const ( DmarcResultPass DmarcResult = "pass" DmarcResultFail DmarcResult = "fail" DmarcResultNone DmarcResult = "none" DmarcResultTemperror DmarcResult = "temperror" DmarcResultPermerror DmarcResult = "permerror" )
type DownloadInfo ¶
type Email ¶
type Email struct {
ID string `json:"id"`
ReceivedAt string `json:"received_at"`
SMTP SMTPEnvelope `json:"smtp"`
Headers EmailHeaders `json:"headers"`
Content EmailContent `json:"content"`
Parsed ParsedData `json:"parsed"`
Analysis EmailAnalysis `json:"analysis"`
Auth EmailAuth `json:"auth"`
}
type EmailAddress ¶
type EmailAnalysis ¶
type EmailAnalysis struct {
// Spamassassin holds SpamAssassin analysis results.
// Optional. Present when the email was processed by a SpamAssassin-equipped
// pipeline (always present in Primitive's managed service).
Spamassassin *SpamAssassinAnalysis `json:"spamassassin,omitempty"`
// Forward holds forward detection and analysis results.
// Optional. Present when the email was processed by a forward-detection
// pipeline (always present in Primitive's managed service).
Forward *ForwardAnalysis `json:"forward,omitempty"`
}
EmailAnalysis contains email analysis and classification results.
All fields are optional (pointer types). Which fields are present depends on the analysis pipeline processing the email. Primitive's managed service populates all fields. Self-hosted or third-party deployments may include some, all, or none of these fields depending on their pipeline configuration.
A nil field means that particular analysis was not performed, not that analysis produced no results.
type EmailAuth ¶
type EmailAuth struct {
SPF SpfResult `json:"spf"`
DMARC DmarcResult `json:"dmarc"`
DMARCPolicy *DmarcPolicy `json:"dmarcPolicy"`
DMARCFromDomain *string `json:"dmarcFromDomain"`
DMARCSpfAligned *bool `json:"dmarcSpfAligned,omitempty"`
DMARCDkimAligned *bool `json:"dmarcDkimAligned,omitempty"`
DMARCSpfStrict *bool `json:"dmarcSpfStrict"`
DMARCDkimStrict *bool `json:"dmarcDkimStrict"`
DKIMSignatures []DKIMSignature `json:"dkimSignatures"`
}
type EmailContent ¶
type EmailContent struct {
Raw RawContent `json:"raw"`
Download DownloadInfo `json:"download"`
}
type EmailHeaders ¶
type EmailReceivedEvent ¶
type EmailReceivedEvent struct {
ID string `json:"id"`
Event string `json:"event"`
Version string `json:"version"`
Delivery Delivery `json:"delivery"`
Email Email `json:"email"`
}
func HandleWebhook ¶
func HandleWebhook(options HandleWebhookOptions) (*EmailReceivedEvent, error)
func ValidateEmailReceivedEvent ¶
func ValidateEmailReceivedEvent(input any) (*EmailReceivedEvent, error)
func (EmailReceivedEvent) GetEvent ¶
func (e EmailReceivedEvent) GetEvent() string
type ErrorDefinition ¶
type ForwardAnalysis ¶
type ForwardAnalysis struct {
Detected bool `json:"detected"`
Results []ForwardResult `json:"results"`
AttachmentsFound int64 `json:"attachments_found"`
AttachmentsAnalyzed int64 `json:"attachments_analyzed"`
AttachmentsLimit *int64 `json:"attachments_limit"`
}
type ForwardOriginalSender ¶
type ForwardResult ¶
type ForwardResult struct {
Type string `json:"type"`
AttachmentTarPath *string `json:"attachment_tar_path,omitempty"`
AttachmentFilename *string `json:"attachment_filename,omitempty"`
Analyzed *bool `json:"analyzed,omitempty"`
OriginalSender *ForwardOriginalSender `json:"original_sender"`
Verification *ForwardVerification `json:"verification"`
Summary string `json:"summary"`
}
func (ForwardResult) MarshalJSON ¶
func (r ForwardResult) MarshalJSON() ([]byte, error)
type ForwardVerdict ¶
type ForwardVerdict string
const ( ForwardVerdictLegit ForwardVerdict = "legit" ForwardVerdictUnknown ForwardVerdict = "unknown" )
type ForwardVerification ¶
type ForwardVerification struct {
Verdict ForwardVerdict `json:"verdict"`
Confidence AuthConfidence `json:"confidence"`
DKIMVerified bool `json:"dkim_verified"`
DKIMDomain *string `json:"dkim_domain"`
DMARCPolicy *DmarcPolicy `json:"dmarc_policy"`
}
type HandleWebhookOptions ¶
type ParsedData ¶
type ParsedData struct {
Status ParsedStatus `json:"status"`
Error *ParsedError `json:"error"`
BodyText *string `json:"body_text"`
BodyHTML *string `json:"body_html"`
ReplyTo []EmailAddress `json:"reply_to"`
CC []EmailAddress `json:"cc"`
BCC []EmailAddress `json:"bcc"`
InReplyTo []string `json:"in_reply_to"`
References []string `json:"references"`
Attachments []WebhookAttachment `json:"attachments"`
AttachmentsDownloadURL *string `json:"attachments_download_url"`
}
type ParsedError ¶
type ParsedStatus ¶
type ParsedStatus string
const ( ParsedStatusComplete ParsedStatus = "complete" ParsedStatusFailed ParsedStatus = "failed" )
type PrimitiveWebhookError ¶
type PrimitiveWebhookError struct {
NameValue string
CodeValue string
MessageValue string
SuggestionValue string
Cause error
}
func (*PrimitiveWebhookError) As ¶
func (e *PrimitiveWebhookError) As(target any) bool
func (*PrimitiveWebhookError) Code ¶
func (e *PrimitiveWebhookError) Code() string
func (*PrimitiveWebhookError) Error ¶
func (e *PrimitiveWebhookError) Error() string
func (*PrimitiveWebhookError) Message ¶
func (e *PrimitiveWebhookError) Message() string
func (*PrimitiveWebhookError) Name ¶
func (e *PrimitiveWebhookError) Name() string
func (*PrimitiveWebhookError) Suggestion ¶
func (e *PrimitiveWebhookError) Suggestion() string
func (*PrimitiveWebhookError) ToMap ¶
func (e *PrimitiveWebhookError) ToMap() map[string]any
func (*PrimitiveWebhookError) Unwrap ¶
func (e *PrimitiveWebhookError) Unwrap() error
type RawContent ¶
type RawContent struct {
Included bool `json:"included"`
Encoding *string `json:"encoding,omitempty"`
ReasonCode *string `json:"reason_code,omitempty"`
MaxInlineBytes int64 `json:"max_inline_bytes"`
SizeBytes int64 `json:"size_bytes"`
SHA256 string `json:"sha256"`
Data *string `json:"data,omitempty"`
}
type RawEmailDecodeError ¶
type RawEmailDecodeError struct{ PrimitiveWebhookError }
func NewRawEmailDecodeError ¶
func NewRawEmailDecodeError(code string, message string) *RawEmailDecodeError
type SMTPEnvelope ¶
type SignResult ¶
type SignResult struct {
Header string `json:"header"`
Timestamp int64 `json:"timestamp"`
V1 string `json:"v1"`
}
func SignWebhookPayload ¶
func SignWebhookPayload(rawBody any, secret any, timestamps ...int64) (SignResult, error)
type SpamAssassinAnalysis ¶
type SpamAssassinAnalysis struct {
Score float64 `json:"score"`
}
type StandardWebhooksSignResult ¶
type StandardWebhooksSignResult struct {
Signature string `json:"signature"`
MsgID string `json:"msg_id"`
Timestamp int64 `json:"timestamp"`
}
func SignStandardWebhooksPayload ¶
func SignStandardWebhooksPayload(rawBody any, secret any, msgID string, timestamps ...int64) (StandardWebhooksSignResult, error)
SignStandardWebhooksPayload signs a payload using the Standard Webhooks format.
type UnknownEvent ¶
type UnknownEvent struct {
Event string `json:"event"`
ID *string `json:"id,omitempty"`
Version *string `json:"version,omitempty"`
Payload map[string]any `json:"-"`
}
func (UnknownEvent) GetEvent ¶
func (e UnknownEvent) GetEvent() string
func (UnknownEvent) MarshalJSON ¶
func (e UnknownEvent) MarshalJSON() ([]byte, error)
func (*UnknownEvent) UnmarshalJSON ¶
func (e *UnknownEvent) UnmarshalJSON(data []byte) error
type ValidateEmailAuthResult ¶
type ValidateEmailAuthResult struct {
Verdict AuthVerdict `json:"verdict"`
Confidence AuthConfidence `json:"confidence"`
Reasons []string `json:"reasons"`
}
func ValidateEmailAuth ¶
func ValidateEmailAuth(input any) (ValidateEmailAuthResult, error)
type ValidationIssue ¶
type ValidationResult ¶
type ValidationResult[T any] struct { Success bool Data T Error *WebhookValidationError }
func SafeValidateEmailReceivedEvent ¶
func SafeValidateEmailReceivedEvent(input any) ValidationResult[*EmailReceivedEvent]
type VerifyOptions ¶
type WebhookAttachment ¶
type WebhookEvent ¶
type WebhookEvent interface {
GetEvent() string
}
func HandleWebhookEvent ¶
func HandleWebhookEvent(options HandleWebhookOptions) (WebhookEvent, error)
func ParseWebhookEvent ¶
func ParseWebhookEvent(input any) (WebhookEvent, error)
type WebhookPayloadError ¶
type WebhookPayloadError struct{ PrimitiveWebhookError }
func NewWebhookPayloadError ¶
func NewWebhookPayloadError(code string, message string, suggestion string, cause error) *WebhookPayloadError
type WebhookValidationError ¶
type WebhookValidationError struct {
PrimitiveWebhookError
Field string
ValidationErrors []ValidationIssue
AdditionalErrorCount int
}
func NewWebhookValidationError ¶
func NewWebhookValidationError(field string, message string, suggestion string, validationErrors []ValidationIssue) *WebhookValidationError
func (*WebhookValidationError) ToMap ¶
func (e *WebhookValidationError) ToMap() map[string]any
type WebhookVerificationError ¶
type WebhookVerificationError struct{ PrimitiveWebhookError }
func NewWebhookVerificationError ¶
func NewWebhookVerificationError(code string, message string, suggestion string) *WebhookVerificationError