primitive

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 21 Imported by: 0

README

github.com/primitivedotdev/sdks/sdk-go

Official Primitive Go SDK.

The package is intentionally centered on a small inbound/outbound email automation flow:

  • primitive.Receive(...)
  • primitive.NewClient(...)
  • client.Send(...)
  • client.Reply(...)
  • client.Forward(...)

The generated HTTP API and lower-level webhook helpers remain available for advanced use.

Requirements

  • Go >=1.25

Installation

go get github.com/primitivedotdev/sdks/sdk-go@latest

Basic usage

Receive and reply
package main

import (
	"context"
	"log"
	"time"

	primitive "github.com/primitivedotdev/sdks/sdk-go"
)

func handle(ctx context.Context, body []byte, headers map[string]string) {
	email, err := primitive.Receive(primitive.HandleWebhookOptions{
		Body:    body,
		Headers: headers,
		Secret:  "whsec_...",
	})
	if err != nil {
		log.Printf("invalid webhook: %v", err)
		return
	}

	client, err := primitive.NewClient("prim_test")
	if err != nil {
		log.Fatal(err)
	}

	_, err = client.Reply(ctx, email, primitive.ReplyParams{BodyText: "Thank you for your email."})
	if err != nil {
		log.Printf("reply failed: %v", err)
	}
}
Send a new email
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
wait := true

result, err := client.Send(ctx, primitive.SendParams{
	From:    "Support <support@example.com>",
	To:      "alice@example.com",
	Subject: "Hello",
	BodyText: "Hi there",
	// Use a unique key per logical send. Reusing a key returns the original
	// response from the first send, which is how retries are deduplicated.
	IdempotencyKey: "customer-key-abc123",
	Wait:           &wait,
	WaitTimeoutMs:  5000,
})

Send, Reply, and Forward keep the HTTP request open until Primitive's downstream SMTP transaction completes. Use a context deadline long enough for SMTP delivery, typically 30-60 seconds.

Per-call timeout and cancellation

Every client method takes ctx context.Context as its first argument, so per-call deadlines, cancellation, and request-scoped values use the standard library directly. There is no separate RequestOptions struct.

// Per-call timeout: cancel after 15 seconds.
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err := client.Send(ctx, primitive.SendParams{
    From:    "Support <support@example.com>",
    To:      "alice@example.com",
    Subject: "Hello",
    BodyText: "Hi there",
})

// Per-call cancellation: bail out from another goroutine.
ctx, cancel := context.WithCancel(context.Background())
go func() { <-userBailoutSignal; cancel() }()
_, err := client.Send(ctx, primitive.SendParams{ /* params */ })

A canceled ctx surfaces as context.Canceled; a deadline exceeded surfaces as context.DeadlineExceeded. Both are distinct from API errors returned as *primitive.APIError, so callers can tell a client-side abort apart from a server response.

For idempotent retries, set IdempotencyKey on SendParams or ForwardParams (see the Send example above). The same key replays the original response.

About Wait mode

When Wait is true, the call returns the first downstream SMTP outcome (or WaitTimeoutMs, default 30000). Possible terminal DeliveryStatus values:

  • delivered accepted by the receiving MTA
  • bounced rejected by the receiving MTA (the response is still 200 OK)
  • deferred temporary failure, the receiving MTA may retry
  • wait_timeout no outcome was observed in time. Treat as "outcome unknown." The send may still complete after the response returns.
Reply from a different address

Reply defaults the From address to the inbound recipient (the address that received the email). When your verified outbound domain differs from your inbound domain, pass From explicitly:

_, err = client.Reply(ctx, email, primitive.ReplyParams{
	BodyText: "Thanks for your email.",
	From:     "notifications@outbound.example.com",
})
HTML replies and waiting on the delivery outcome

Reply accepts BodyHTML as a sibling of BodyText, plus the same Wait flag the top-level Send takes:

wait := true
_, err = client.Reply(ctx, email, primitive.ReplyParams{
	BodyText: "Thanks for your email.",
	BodyHTML: "<p>Thanks for your email.</p>",
	Attachments: []primitive.SendAttachment{
		{
			Filename:      "report.txt",
			ContentBase64: "aGVsbG8=",
		},
	},
	Wait:     &wait,
})

A subject override is intentionally not exposed on ReplyParams. Gmail's Conversation View needs both a References match and a normalized-subject match to thread, so a custom subject silently breaks the thread for half the recipient population. Use client.Send(...) if you need full subject control.

If the inbound row is not in a state we can reply to (no Message-Id recorded, or content was discarded), the API returns inbound_not_repliable (HTTP 422) and the SDK returns an error.

Forward an inbound email
_, err = client.Forward(context.Background(), email, primitive.ForwardParams{
	To:       "ops@example.com",
	BodyText: "Can you take this one?",
})

The normalized email object

primitive.Receive(...) returns a normalized inbound email object with fields such as:

email.Sender.Address
email.ReceivedBy
email.ReplyTarget.Address
email.ReplySubject
email.ForwardSubject
email.Subject
email.Text
email.Thread.MessageID
email.Thread.References
email.Raw

Advanced usage

Generated API package

Use the sibling api package when you want the full generated HTTP API surface.

import primitiveapi "github.com/primitivedotdev/sdks/sdk-go/api"

client, err := primitiveapi.NewAPIClient("prim_test")
Lower-level webhook helpers

Advanced users can still work directly with:

  • HandleWebhook(...)
  • HandleWebhookEvent(...)
  • ParseWebhookEvent(...)
  • VerifyWebhookSignature(...)

Development

From sdks/sdk-go:

go test ./...
go test -run TestSharedCompatibilityFixtures ./...
gofmt -w .

Or from repo root sdks/:

make go-generate
make go-check
make go-build

Documentation

Overview

Package primitive provides a small, high-level inbound/outbound email SDK for Primitive.

The main surface is centered around Receive for inbound webhooks and Client for outbound send/reply/forward operations.

Lower-level webhook and generated API helpers still remain available for advanced use cases.

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 HandleWebhook, VerifyWebhookSignature, ParseWebhookEvent, or ValidateEmailReceivedEvent directly, or use the generated API client in the sibling api package.

Unknown future event types are preserved as UnknownEvent values so consumers can continue receiving webhook traffic before a package update ships.

Index

Constants

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

View Source
var EmailReceivedEventJSONSchema map[string]any
View Source
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.",
	},
}
View Source
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.",
	},
}
View Source
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 BuildForwardSubject added in v0.8.0

func BuildForwardSubject(subject string) string

func BuildReplySubject added in v0.8.0

func BuildReplySubject(subject string) string

func ConfirmedHeaders

func ConfirmedHeaders() map[string]string

func DecodeRawEmail

func DecodeRawEmail(event any, verify ...bool) ([]byte, error)

func FormatAddress added in v0.8.0

func FormatAddress(address ReceivedEmailAddress) string

func GetDownloadTimeRemaining

func GetDownloadTimeRemaining(event any, nowMillis ...int64) (int64, error)

func IsDownloadExpired

func IsDownloadExpired(event any, nowMillis ...int64) (bool, error)

func IsEmailReceivedEvent

func IsEmailReceivedEvent(event any) bool

func IsRawIncluded

func IsRawIncluded(event any) (bool, error)

func ParseJSONBody

func ParseJSONBody(rawBody any) (any, error)

func PrepareStandardWebhooksSecret

func PrepareStandardWebhooksSecret(secret any) ([]byte, error)

PrepareStandardWebhooksSecret strips the "whsec_" prefix if present, then base64-decodes the remainder to produce the raw HMAC key bytes.

func VerifyRawEmailDownload

func VerifyRawEmailDownload(downloaded []byte, event any) ([]byte, error)

func VerifyStandardWebhooksSignature

func VerifyStandardWebhooksSignature(options StandardWebhooksVerifyOptions) (bool, error)

VerifyStandardWebhooksSignature verifies a Standard Webhooks signature.

func VerifyWebhookSignature

func VerifyWebhookSignature(options VerifyOptions) (bool, error)

Types

type APIError added in v0.8.0

type APIError struct {
	StatusCode int
	Code       string
	Message    string
	RetryAfter *int
	Gates      []primitiveapi.GateDenial
	RequestID  string
	Details    *primitiveapi.ErrorResponseErrorDetails
	Payload    any
}

func (*APIError) Error added in v0.8.0

func (e *APIError) Error() string

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 Client added in v0.8.0

type Client struct {
	// contains filtered or unexported fields
}

Client is the high-level Primitive SDK client. Internally it constructs two generated clients (one per host) and routes each operation to the right one so customers don't have to think about the host split:

  • api / Host 1 (DefaultAPIBaseURL1, "https://www.primitive.dev/api/v1"): every operation except attachment-capable message sends.
  • apiSend / Host 2 (DefaultAPIBaseURL2, "https://api.primitive.dev/v1"): /send-mail and /emails/{id}/reply. Cloudflare Worker with a larger request body cap to support attachment sends and replies.

func NewClient added in v0.8.0

func NewClient(apiKey string, opts ...primitiveapi.ClientOption) (*Client, error)

func NewClientFromAPI added in v0.8.0

func NewClientFromAPI(apiClient sendAPI) *Client

NewClientFromAPI wraps a customer-supplied generated client. Useful for tests where the customer wants full control over the underlying HTTP layer. The same client is used for both host-1 and host-2 operations; the caller is responsible for ensuring it points at a host that can serve both shapes (typically only happens in tests against a mock).

func NewClientWithOptions added in v0.21.0

func NewClientWithOptions(apiKey string, options ClientOptions) (*Client, error)

NewClientWithOptions builds a Client with explicit base-URL overrides. Prefer NewClient unless you are running against staging or local.

func (*Client) Forward added in v0.8.0

func (c *Client) Forward(ctx context.Context, email *ReceivedEmail, input ForwardParams) (SendResult, error)

func (*Client) Reply added in v0.8.0

func (c *Client) Reply(ctx context.Context, email *ReceivedEmail, input ReplyParams) (SendResult, error)

Reply sends an outbound reply to an inbound email.

Calls POST /emails/{id}/reply on the server. Recipients, subject, and threading headers are derived server-side from the inbound row identified by email.ID. The customer controls the body, optional From override, optional attachments, and optional Wait flag.

func (*Client) SemanticSearch added in v0.35.0

SemanticSearch runs a semantic / hybrid / keyword search across received and sent mail (POST /v1/semantic-search on the search host). Returns ranked rows; each row carries matched fields, a match-centered excerpt, and an additive score breakdown. See primitiveapi.SemanticSearchInput for request fields and primitiveapi.SemanticSearchResult for the row shape.

Requires the Pro plan and the semantic_search_enabled entitlement; callers without them receive an APIError with Status: 403.

func (*Client) Send added in v0.8.0

func (c *Client) Send(ctx context.Context, params SendParams) (SendResult, error)

Send sends an outbound email. The request remains open until Primitive's downstream SMTP transaction completes, so callers should pass a context with a deadline long enough for SMTP delivery, typically 30-60 seconds.

type ClientOptions added in v0.21.0

type ClientOptions struct {
	// APIBaseURL1 overrides the primary API host. Empty = production default.
	APIBaseURL1 string
	// APIBaseURL2 overrides the attachments-supporting send host. Empty = production default.
	APIBaseURL2 string
	// Extra options forwarded to both underlying ogen clients (TLS, HTTP
	// client, telemetry middleware, etc.).
	Extra []primitiveapi.ClientOption
}

ClientOptions configures a NewClient call. Both base URLs default to the production hosts and only need overriding for internal staging/local testing. The overrides are not part of the publicly- documented SDK surface.

type DKIMSignature

type DKIMSignature struct {
	Domain   string     `json:"domain"`
	Selector *string    `json:"selector,omitempty"`
	Result   DkimResult `json:"result"`
	Aligned  bool       `json:"aligned"`
	KeyBits  *int64     `json:"keyBits,omitempty"`
	Algo     *string    `json:"algo,omitempty"`
}

type Delivery

type Delivery struct {
	EndpointID  string `json:"endpoint_id"`
	Attempt     int64  `json:"attempt"`
	AttemptedAt string `json:"attempted_at"`
}

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 DownloadInfo struct {
	URL       string `json:"url"`
	ExpiresAt string `json:"expires_at"`
}

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 EmailAddress struct {
	Address string  `json:"address"`
	Name    *string `json:"name"`
}

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 EmailHeaders struct {
	MessageID *string `json:"message_id"`
	Subject   *string `json:"subject"`
	From      string  `json:"from"`
	To        string  `json:"to"`
	Date      *string `json:"date"`
}

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 ErrorDefinition struct {
	Message    string
	Suggestion string
}

type EventType

type EventType string
const EventTypeEmailReceived EventType = "email.received"

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 ForwardOriginalSender struct {
	Email  string `json:"email"`
	Domain string `json:"domain"`
}

type ForwardParams added in v0.8.0

type ForwardParams struct {
	To             string
	BodyText       string
	Subject        string
	From           string
	IdempotencyKey string
}

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 HandleWebhookOptions struct {
	Body             any
	Headers          any
	Secret           any
	ToleranceSeconds *int64
}

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"`
	ToAddresses            []EmailAddress      `json:"to_addresses"`
	InReplyTo              []string            `json:"in_reply_to"`
	References             []string            `json:"references"`
	Attachments            []WebhookAttachment `json:"attachments"`
	AttachmentsDownloadURL *string             `json:"attachments_download_url"`
}

type ParsedError

type ParsedError struct {
	Code      string `json:"code"`
	Message   string `json:"message"`
	Retryable bool   `json:"retryable"`
}

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 ReceiveRequestOptions added in v0.8.0

type ReceiveRequestOptions struct {
	Secret           string
	ToleranceSeconds *int64
}

type ReceivedEmail added in v0.8.0

type ReceivedEmail struct {
	ID             string
	EventID        string
	ReceivedAt     string
	Sender         ReceivedEmailAddress
	ReplyTarget    ReceivedEmailAddress
	ReceivedBy     string
	ReceivedByAll  []string
	Subject        string
	ReplySubject   string
	ForwardSubject string
	Text           string
	Thread         ReceivedEmailThread
	Attachments    []WebhookAttachment
	Auth           EmailAuth
	Analysis       EmailAnalysis
	Raw            EmailReceivedEvent
}

func NormalizeReceivedEmail added in v0.8.0

func NormalizeReceivedEmail(event EmailReceivedEvent) (*ReceivedEmail, error)

NormalizeReceivedEmail builds a ReceivedEmail from a validated webhook event. It returns an error rather than panicking when required fields (SMTP recipients) are missing so callers running with hand-built events, replays, or test fixtures get a recoverable failure instead of a process crash.

func Receive added in v0.8.0

func Receive(options HandleWebhookOptions) (*ReceivedEmail, error)

func ReceiveFromHTTPRequest added in v0.8.0

func ReceiveFromHTTPRequest(request *http.Request, options ReceiveRequestOptions) (*ReceivedEmail, error)

type ReceivedEmailAddress added in v0.8.0

type ReceivedEmailAddress struct {
	Address string
	Name    string
}

func ParseHeaderAddress added in v0.8.0

func ParseHeaderAddress(value string) *ReceivedEmailAddress

ParseHeaderAddress parses a single RFC 5322 header address (From, Sender, Reply-To). Lenient about quirky headers (unquoted commas in display names, missing closing angle brackets) but strict about the resulting address: the extracted addr-spec must look like a real email or this returns nil and the normalizer falls back to the SMTP envelope sender.

type ReceivedEmailThread added in v0.8.0

type ReceivedEmailThread struct {
	MessageID  string
	InReplyTo  []string
	References []string
}

type ReplyParams added in v0.8.0

type ReplyParams struct {
	BodyText    string
	BodyHTML    string
	From        string
	Attachments []SendAttachment
	Wait        *bool
}

ReplyParams is the input shape for Client.Reply.

Recipients (To), subject ("Re: <parent>"), and threading headers (In-Reply-To, References) are derived server-side from the inbound row referenced by the email's ID. Subject overrides are not supported because Gmail's Conversation View needs both a References match and a normalized-subject match to thread, and a custom subject silently breaks that.

type SMTPEnvelope

type SMTPEnvelope struct {
	Helo     *string  `json:"helo"`
	MailFrom string   `json:"mail_from"`
	RcptTo   []string `json:"rcpt_to"`
}

type SemanticSearchResponse added in v0.35.0

type SemanticSearchResponse struct {
	Data []primitiveapi.SemanticSearchResult
	Meta primitiveapi.SemanticSearchMeta
}

SemanticSearchResponse is the hand-written return type from Client.SemanticSearch. Mirrors the server's success envelope (data + meta) without the boolean success flag, since errors surface as Go errors instead.

type SendAttachment added in v0.35.0

type SendAttachment = primitiveapi.SendMailAttachment

type SendParams added in v0.8.0

type SendParams struct {
	From           string
	To             string
	Subject        string
	BodyText       string
	BodyHTML       string
	Thread         *SendThread
	Wait           *bool
	WaitTimeoutMs  int
	IdempotencyKey string
}

type SendResult added in v0.8.0

type SendResult struct {
	ID                   string
	Status               primitiveapi.SentEmailStatus
	QueueID              primitiveapi.NilString
	Accepted             []string
	Rejected             []string
	ClientIdempotencyKey string
	RequestID            string
	ContentHash          string
	// IdempotentReplay is true when the response replays a previously
	// recorded send keyed by ClientIdempotencyKey (same key, same
	// canonical payload). False on a fresh send and on gate-denied
	// responses.
	IdempotentReplay bool
	DeliveryStatus   primitiveapi.OptDeliveryStatus
	SMTPResponseCode primitiveapi.OptNilInt
	SMTPResponseText primitiveapi.OptString
}

type SendThread added in v0.8.0

type SendThread struct {
	InReplyTo  string
	References []string
}

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 SpfResult

type SpfResult string
const (
	SpfResultPass      SpfResult = "pass"
	SpfResultFail      SpfResult = "fail"
	SpfResultSoftfail  SpfResult = "softfail"
	SpfResultNeutral   SpfResult = "neutral"
	SpfResultNone      SpfResult = "none"
	SpfResultTemperror SpfResult = "temperror"
	SpfResultPermerror SpfResult = "permerror"
)

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 StandardWebhooksVerifyOptions

type StandardWebhooksVerifyOptions struct {
	RawBody          any
	MsgID            string
	Timestamp        string
	SignatureHeader  string
	Secret           any
	ToleranceSeconds *int64
	NowSeconds       *int64
}

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 ValidationIssue struct {
	Path      string `json:"path"`
	Message   string `json:"message"`
	Validator string `json:"validator"`
}

type ValidationResult

type ValidationResult[T any] struct {
	Success bool
	Data    T
	Error   *WebhookValidationError
}

func SafeValidateEmailReceivedEvent

func SafeValidateEmailReceivedEvent(input any) ValidationResult[*EmailReceivedEvent]

type VerifyOptions

type VerifyOptions struct {
	RawBody          any
	SignatureHeader  string
	Secret           any
	ToleranceSeconds *int64
	NowSeconds       *int64
}

type WebhookAttachment

type WebhookAttachment struct {
	Filename    *string `json:"filename"`
	ContentType string  `json:"content_type"`
	SizeBytes   int64   `json:"size_bytes"`
	SHA256      string  `json:"sha256"`
	PartIndex   int64   `json:"part_index"`
	TarPath     string  `json:"tar_path"`
}

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

Directories

Path Synopsis
Package api provides a generated client for the Primitive HTTP API.
Package api provides a generated client for the Primitive HTTP API.

Jump to

Keyboard shortcuts

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