notifications

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 25 Imported by: 0

README

thinwrap/notifications (Go)

⚠️ Work in progress (v0). A faithful Go port of @thinwrap/notifications (TypeScript) and thinwrap/notifications (PHP): one unified facade per channel over 35 providers across 4 channels, with the same normalized surface and error model.

Unified, SDK-free, zero-dependency Go wrapper for sending email, SMS, push, and chat across 35 providers. Switch vendor by changing the config type; the input and output shapes stay identical. Stateless — bring your own *http.Client, no vendor SDKs.

import "github.com/thinwrap/notifications-go"

Requires Go ≥ 1.18. No third-party dependencies (standard library only) — AWS SigV4 (SES/SNS) and JWT signing (FCM, APNs) are hand-rolled on crypto/*.

Two-minute send

e := notifications.NewEmail(notifications.SendgridConfig{
    APIKey: os.Getenv("SENDGRID_API_KEY"),
    From:   "hello@acme.com",
})

res, err := e.Send(ctx, notifications.EmailSendInput{
    To:      "user@example.com",
    Subject: "Welcome",
    Text:    "Thanks for signing up!",
    HTML:    "<p>Thanks for signing up!</p>",
})
if err != nil {
    var ce *notifications.ConnectorError
    if errors.As(err, &ce) {
        log.Printf("%s: %s", ce.ProviderCode, ce.ProviderMessage)
    }
    return
}
fmt.Printf("status=%s id=%s\n", res.Status, res.ProviderMessageID)

Channels & providers

The config type selects the provider. A provider that doesn't serve a channel is unrepresentable at compile time (e.g. TwilioConfig does not satisfy EmailConfig, so NewEmail(TwilioConfig{...}) will not compile).

Facade (constructor) Method Providers
NewEmail Send SES, Resend, Mailgun, SendGrid, Postmark, MailerSend, Mailtrap, Brevo, SparkPost, Scaleway
NewSms Send Vonage, Twilio, Plivo, SNS, Sinch, Telnyx, Infobip, MessageBird, TextMagic, D7 Networks
NewPush Send FCM, Expo, APNs, OneSignal, Pusher Beams, WonderPush
NewChat Send Telegram, Slack, WhatsApp Business, Discord, MS Teams, Google Chat, Mattermost, Rocket.Chat, LINE

Per-provider details (endpoint, auth, error mapping, passthrough keys) live in docs/providers/ — one page per provider.

Switching providers

// Same Send(ctx, in) call, same EmailSendResult — only the config changes.
e := notifications.NewEmail(notifications.ResendConfig{APIKey: key, From: "hi@acme.com"})
// or notifications.PostmarkConfig{...}, notifications.SesConfig{...}, ...

The normalized surface

Every channel's Send returns (*<Channel>SendResult, error):

type EmailSendResult struct {
    Success           bool
    Status            SendStatus // sent | queued | rejected | suppressed | unknown
    ProviderMessageID string
    Raw               any        // decoded vendor body, verbatim
}

Only fields ≥90% of a channel's providers support are first-class on the input (the ≥90% baseline-coverage rule). Provider-specific fields flow through the Passthrough escape hatch:

res, err := s.Send(ctx, notifications.SmsSendInput{
    To:   "+15552223333",
    Body: "hi",
    Passthrough: &notifications.Passthrough{
        Body: map[string]any{"mediaUrl": []any{"https://cdn/img.png"}}, // Twilio MMS
    },
})

Passthrough.Body is deep-merged into the request body, Headers/Query shallow-merged; consumer values win on collision.

Errors

Every failure is a *ConnectorError, reachable with errors.As:

var ce *notifications.ConnectorError
if errors.As(err, &ce) {
    // ce.ProviderCode  — invalid_recipient | rate_limited | auth_failed |
    //                    provider_unavailable | invalid_request | unknown
    // ce.StatusCode    — *int (nil for transport failures)
    // ce.ProviderMessage
    // ce.Cause         — {"raw": <vendor body>, "retryAfter": <hdr>, "retryAfterSeconds": <n>}
}

There is deliberately no top-level RetryAfterSeconds — the raw Retry-After header and its parsed seconds ride in Cause. Retry is consumer policy.

Bring your own HTTP client

e := notifications.NewEmail(cfg, notifications.WithHTTPClient(myClient))

myClient satisfies interface{ Do(*http.Request) (*http.Response, error) }. The default client never follows redirects. The wrapper is stateless: no caching, retries, or telemetry. FCM/APNs sign a fresh token on every Send unless you supply a TokenCacheHook on the config.

Relationship to the siblings

This is one of four thinwrap notifications libraries sharing a normalized surface and byte-identical provider ids / error codes:

License

MIT © Dmitry Polyanovsky & contributors

Documentation

Overview

Package notifications is a lightweight, SDK-free, zero-dependency Go wrapper for sending email, SMS, push, and chat messages across 35 providers behind one normalized surface.

It is the Go sibling of @thinwrap/notifications (npm) and thinwrap/notifications (Packagist), sharing the same normalized surface: four channel facades (Email, Sms, Push, Chat), a single typed *ConnectorError, and a Passthrough escape hatch for provider-specific fields.

Construct a channel facade from a provider config and call Send:

e := notifications.NewEmail(notifications.SendgridConfig{APIKey: key, From: "hi@acme.com"})
res, err := e.Send(ctx, notifications.EmailSendInput{
	To:      "user@example.com",
	Subject: "Welcome",
	Text:    "Hello!",
})
if err != nil {
	var ce *notifications.ConnectorError
	if errors.As(err, &ce) {
		// ce.ProviderCode, ce.StatusCode, ce.ProviderMessage
	}
}

The config type selects the provider; switching vendors is a one-line change (swap SendgridConfig for ResendConfig, etc.). A config value only satisfies the facade constructor for the channel it serves, so an unsupported (provider, channel) pair is unrepresentable at the call site.

The wrapper holds no state: no caching, retries, idempotency keys, or telemetry. FCM and APNs sign fresh on every Send unless the consumer supplies a TokenCacheHook on the provider config. Bring your own HTTP client via WithHTTPClient; the default client never follows redirects.

Zero third-party dependencies: SigV4 (SES/SNS) and JWT signing (FCM RS256, APNs ES256) are hand-rolled on the standard library.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ApnsConfig

type ApnsConfig struct {
	// TeamID is the 10-char Apple Developer Team ID (the JWT iss claim).
	TeamID string
	// KeyID is the 10-char APNs auth-key ID (the JWT kid header).
	KeyID string
	// PrivateKey is the PKCS#8 PEM-encoded EC P-256 private key (the .p8 file
	// contents). It signs the ES256 auth JWT.
	PrivateKey string
	// BundleID is the app bundle identifier used as the default apns-topic
	// header. Per-send override via a Passthrough.Headers "apns-topic" entry.
	BundleID string
	// Env selects the host: "production" -> api.push.apple.com, anything else
	// (including "sandbox") -> api.sandbox.push.apple.com.
	Env string
	// TokenCache is an optional consumer-provided cache for the signed ES256
	// JWT. When nil the wrapper signs fresh on every Send (the stateless
	// default). The cache key is "apns:"+TeamID+":"+KeyID+":"+BundleID. On a
	// vendor 403 the wrapper does NOT evict — eviction is the consumer's job.
	TokenCache TokenCacheHook
}

ApnsConfig authenticates the Apple Push Notification service provider via token-based (.p8) auth. Fields map 1:1 to apns.config.ts.

func (ApnsConfig) ProviderID

func (ApnsConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type BrevoConfig

type BrevoConfig struct {
	// APIKey is the Brevo API key (sent in the `api-key` request header, NOT as
	// `Authorization: Bearer …`).
	APIKey string
	// From is the default sender email address (Brevo's sender.email).
	From string
	// SenderName is an optional display name composed alongside From as
	// sender: { name, email }.
	SenderName string
}

BrevoConfig authenticates the Brevo email provider.

func (BrevoConfig) ProviderID

func (BrevoConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type Casing

type Casing int

Casing is the target key-casing for transformKeys. Connectors whose wire bodies use a non-camelCase convention (SendGrid = snake_case, Twilio = PascalCase) apply transformKeys to the consumer's Passthrough.Body so camelCase passthrough keys land in the vendor's expected casing.

const (
	CamelCase Casing = iota
	PascalCase
	SnakeCase
	KebabCase
	ConstantCase
)

type ChannelType

type ChannelType string

ChannelType is the notification channel a provider serves. The string values are byte-identical across the thinwrap notifications siblings (TypeScript, PHP, Go).

const (
	ChannelEmail ChannelType = "email"
	ChannelSMS   ChannelType = "sms"
	ChannelChat  ChannelType = "chat"
	ChannelPush  ChannelType = "push"
)

type Chat

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

Chat is the unified chat facade. Construct it with NewChat.

func NewChat

func NewChat(cfg ChatConfig, opts ...Option) *Chat

NewChat builds a chat facade for the provider selected by cfg's type.

func (*Chat) ChannelType

func (c *Chat) ChannelType() ChannelType

ChannelType is always ChannelChat.

func (*Chat) ProviderID

func (c *Chat) ProviderID() ProviderID

ProviderID returns the provider this facade dispatches to.

func (*Chat) Send

func (c *Chat) Send(ctx context.Context, in ChatSendInput) (*ChatSendResult, error)

Send delivers a chat message through the configured provider.

type ChatConfig

type ChatConfig interface {
	ProviderID() ProviderID
	// contains filtered or unexported methods
}

ChatConfig is implemented by every chat-provider config value.

type ChatSendInput

type ChatSendInput struct {
	To          string
	Body        string
	Passthrough *Passthrough
}

ChatSendInput is the normalized, ≥90%-baseline chat payload. To is the channel / room / recipient target (optional for webhook-bound connectors that encode the target in the URL). Rich content (blocks, attachments, threads) rides in Passthrough.

type ChatSendResult

type ChatSendResult struct {
	Success           bool
	Status            SendStatus
	ProviderMessageID string
	Raw               any
}

ChatSendResult is the normalized result of a chat send.

type ConnectorError

type ConnectorError struct {
	// StatusCode is the HTTP status, or nil for pre-response / transport failures.
	StatusCode *int
	// ProviderCode is the normalized classification.
	ProviderCode ProviderCode
	// ProviderMessage is the vendor-supplied message (or "" when absent).
	ProviderMessage string
	// Message is the human-readable error message; falls back to ProviderMessage.
	Message string
	// Cause holds the decoded vendor body wrapper (map with "raw") OR an
	// underlying error (transport / encoding failures).
	Cause any
}

ConnectorError is the single typed error every Send can return. Retrieve it from a returned error with errors.As:

var ce *notifications.ConnectorError
if errors.As(err, &ce) { ... }

There is deliberately no top-level RetryAfterSeconds field: for a vendor HTTP error, the raw Retry-After header rides in Cause under key "retryAfter" and its parsed seconds under "retryAfterSeconds" (both siblings converge on this Cause shape: {raw, retryAfter?, retryAfterSeconds?}).

func (*ConnectorError) Error

func (e *ConnectorError) Error() string

func (*ConnectorError) Unwrap

func (e *ConnectorError) Unwrap() error

Unwrap exposes an underlying error cause (e.g. a transport error) to errors.Is / errors.As.

type D7NetworksConfig

type D7NetworksConfig struct {
	// APIToken is the D7 API token (sent as a Bearer credential).
	APIToken string
	// From is the default originator (alphanumeric or E.164); per-send From overrides it.
	From string
}

D7NetworksConfig authenticates the D7 Networks SMS provider. D7's Messaging API uses Bearer-token auth against a single global endpoint (no regional clustering).

func (D7NetworksConfig) ProviderID

func (D7NetworksConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type DiscordConfig

type DiscordConfig struct {
	// WebhookURL is a Discord Incoming Webhook URL — it IS the credential.
	WebhookURL string
}

DiscordConfig authenticates the Discord Incoming Webhook chat provider. This is webhook-URL-as-auth: the URL itself IS the credential and targets the channel directly, so ChatSendInput.To is unused.

func (DiscordConfig) ProviderID

func (DiscordConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type Email

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

Email is the unified email facade. Construct it with NewEmail.

func NewEmail

func NewEmail(cfg EmailConfig, opts ...Option) *Email

NewEmail builds an email facade for the provider selected by cfg's type.

func (*Email) ChannelType

func (e *Email) ChannelType() ChannelType

ChannelType is always ChannelEmail.

func (*Email) ProviderID

func (e *Email) ProviderID() ProviderID

ProviderID returns the provider this facade dispatches to.

func (*Email) Send

Send delivers an email through the configured provider.

type EmailAttachment

type EmailAttachment struct {
	Filename    string
	ContentType string
	Content     []byte
	// ContentID marks the attachment inline (referenced by cid: in the HTML).
	ContentID string
}

EmailAttachment is a single email attachment. Content is the raw bytes; connectors base64-encode it for the vendor wire shape.

type EmailConfig

type EmailConfig interface {
	ProviderID() ProviderID
	// contains filtered or unexported methods
}

EmailConfig is implemented by every email-provider config value. The config's type both selects the provider and builds its connector, so an unsupported (provider, channel) pair is unrepresentable at the call site.

type EmailSendInput

type EmailSendInput struct {
	From        string
	To          string
	Cc          []string
	Bcc         []string
	ReplyTo     string
	Subject     string
	Text        string
	HTML        string
	Attachments []EmailAttachment
	Headers     map[string]string
	Tags        []string
	Passthrough *Passthrough
}

EmailSendInput is the normalized, ≥90%-baseline email payload. Provider- specific fields (SES ConfigurationSetName, Mailgun options, …) ride in Passthrough or on the provider Config.

type EmailSendResult

type EmailSendResult struct {
	Success           bool
	Status            SendStatus
	ProviderMessageID string
	Raw               any
}

EmailSendResult is the normalized result of an email send.

type ExpoConfig

type ExpoConfig struct {
	// AccessToken is an optional Expo access token, used verbatim as a Bearer
	// token when set (Expo accepts unauthenticated requests for projects that
	// allow them). Long-lived per-project key — no refresh, no caching.
	AccessToken string
}

ExpoConfig authenticates the Expo push provider.

func (ExpoConfig) ProviderID

func (ExpoConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type FcmConfig

type FcmConfig struct {
	// ProjectID is the Firebase / GCP project id targeted by the send URL.
	ProjectID string
	// ClientEmail is the service-account client_email (the JWT iss claim).
	ClientEmail string
	// PrivateKey is the service-account private_key: a PEM-encoded RSA private
	// key (PKCS#1 or PKCS#8). It signs the RS256 OAuth assertion JWT.
	PrivateKey string
	// TokenCache is an optional consumer-provided cache for the OAuth access
	// token. When nil the wrapper signs + exchanges fresh on every Send (the
	// stateless default). The cache key is "fcm:" + ProjectID + ":" +
	// ClientEmail, so rotating the service account (new ClientEmail) never
	// serves a token minted for the old one. On a vendor 401 the wrapper does
	// NOT evict — eviction is the consumer's job.
	TokenCache TokenCacheHook
}

FcmConfig authenticates the Firebase Cloud Messaging push provider. Its fields map 1:1 to a Google service-account JSON file (download from the Firebase console).

func (FcmConfig) ProviderID

func (FcmConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type GoogleChatConfig

type GoogleChatConfig struct {
	// WebhookURL is the Google Chat incoming-webhook URL — it IS the credential
	// and targets the destination space directly (there is no separate recipient).
	WebhookURL string
}

GoogleChatConfig authenticates the Google Chat provider via an incoming-webhook URL. Webhook-URL-as-auth: the WebhookURL (including its ?key=&token= query params) IS the credential — leaking it grants posting authority on the space.

func (GoogleChatConfig) ProviderID

func (GoogleChatConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient is the bring-your-own HTTP seam. It is satisfied by *http.Client out of the box; inject any implementation for tracing, retries, mocking, or proxying.

type InfobipConfig

type InfobipConfig struct {
	// BaseURL is the per-account Infobip subdomain (e.g. "xyz123.api.infobip.com").
	// Do NOT include a scheme or trailing slash; the connector adds "https://".
	BaseURL string
	// APIKey authenticates via Infobip's custom "Authorization: App <apiKey>" scheme.
	APIKey string
	// From is the default sender; per-send From overrides it.
	From string
}

InfobipConfig authenticates the Infobip SMS provider. Infobip is the only SMS provider with a per-account base URL: Infobip provisions a customer-specific subdomain when an account is created, so BaseURL is REQUIRED.

func (InfobipConfig) ProviderID

func (InfobipConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type LineConfig

type LineConfig struct {
	// ChannelAccessToken is a long-lived Channel Access Token from the LINE
	// Developers Console (sent as a Bearer credential).
	ChannelAccessToken string
}

LineConfig authenticates the LINE Messaging API (Push) chat provider.

func (LineConfig) ProviderID

func (LineConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MailerSendConfig

type MailerSendConfig struct {
	// APIToken is the MailerSend API token (Bearer secret).
	APIToken string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional default sender display name composed alongside From.
	SenderName string
}

MailerSendConfig authenticates the MailerSend email provider.

func (MailerSendConfig) ProviderID

func (MailerSendConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MailgunConfig

type MailgunConfig struct {
	// APIKey is the Mailgun API key, used as the Basic-auth password against the
	// username "api" (or Username).
	APIKey string
	// Domain is the sending domain (e.g. "mg.example.com"); forms the URL path
	// /v3/<domain>/messages.
	Domain string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed as "Name <addr>".
	SenderName string
	// Region selects the endpoint: "eu" routes to api.eu.mailgun.net; anything
	// else (default) routes to api.mailgun.net. Ignored when BaseURL is set.
	Region string
	// Username is the Basic-auth username; defaults to "api" when empty.
	Username string
	// BaseURL is an explicit endpoint base (e.g. "https://example.local") that
	// takes precedence over Region. Path /v3/<domain>/messages is appended.
	BaseURL string
}

MailgunConfig authenticates the Mailgun email provider.

func (MailgunConfig) ProviderID

func (MailgunConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MailtrapConfig

type MailtrapConfig struct {
	// APIToken is the Mailtrap API token (Bearer credential).
	APIToken string
	// Mode selects the endpoint: "sandbox" captures the mail in an inbox and
	// requires InboxID; "production" actually delivers and forbids InboxID.
	// REQUIRED — there is no default (the sandbox↔production failure mode is
	// asymmetric, so consumers must pick deliberately).
	Mode string
	// InboxID is REQUIRED when Mode=="sandbox" and FORBIDDEN when
	// Mode=="production". Validated at send time.
	InboxID string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional default sender display name.
	SenderName string
}

MailtrapConfig authenticates the Mailtrap email provider.

func (MailtrapConfig) ProviderID

func (MailtrapConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MattermostConfig

type MattermostConfig struct {
	// WebhookURL is a Mattermost Incoming Webhook URL — it IS the credential.
	WebhookURL string
}

MattermostConfig authenticates the Mattermost Incoming Webhook chat provider. This is webhook-URL-as-auth: the URL itself IS the credential and binds the default channel. ChatSendInput.To is an optional channel override.

func (MattermostConfig) ProviderID

func (MattermostConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MessagebirdConfig

type MessagebirdConfig struct {
	// AccessKey authenticates via the custom "Authorization: AccessKey <accessKey>" scheme.
	AccessKey string
	// From is the default sender (originator); per-send From overrides it.
	From string
}

MessagebirdConfig authenticates the MessageBird (now branded "Bird") SMS provider. The legacy rest.messagebird.com surface is the v1.0 baseline; the provider id stays "messagebird".

func (MessagebirdConfig) ProviderID

func (MessagebirdConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type MsTeamsConfig

type MsTeamsConfig struct {
	// WebhookURL is the MS Teams Incoming Webhook URL — it IS the credential and
	// targets the destination channel directly (there is no separate recipient).
	WebhookURL string
}

MsTeamsConfig authenticates the Microsoft Teams chat provider via an Incoming Webhook URL. Webhook-URL-as-auth: the WebhookURL itself IS the credential — leaking it grants posting authority on the destination channel.

func (MsTeamsConfig) ProviderID

func (MsTeamsConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type OneSignalConfig

type OneSignalConfig struct {
	// AppID is the UUID app id from the OneSignal dashboard.
	AppID string
	// APIKey is the REST API key from the OneSignal dashboard (NOT the
	// user-auth key). Sent verbatim as "Authorization: Basic <APIKey>".
	APIKey string
}

OneSignalConfig authenticates the OneSignal push provider.

func (OneSignalConfig) ProviderID

func (OneSignalConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type Option

type Option func(*facadeOptions)

Option configures a channel facade constructor.

func WithHTTPClient

func WithHTTPClient(c HTTPClient) Option

WithHTTPClient injects a custom HTTP client (see HTTPClient). Without it, a non-redirect-following default is used.

type Passthrough

type Passthrough struct {
	Body    map[string]any    `json:"body,omitempty"`
	Headers map[string]string `json:"headers,omitempty"`
	Query   map[string]string `json:"query,omitempty"`
}

Passthrough is the per-send input escape hatch. Keys are forwarded verbatim: Body is deep-merged into the connector's request body, Headers and Query are shallow-merged. Consumer values intentionally OVERRIDE connector-set values (including auth) — there is deliberately no reserved-key protection.

It is the Go analogue of the TypeScript sibling's `_passthrough` and the PHP sibling's passthrough array; it carries the long tail of provider-specific fields that the ≥90% baseline facade does not promote.

type PlivoConfig

type PlivoConfig struct {
	// AuthID is the Plivo Account Auth ID (used in the URL path and as the Basic auth username).
	AuthID string
	// AuthToken is the Plivo Auth Token (Basic auth password).
	AuthToken string
	// From is the default sender (E.164 or short code); per-send From overrides it.
	From string
}

PlivoConfig authenticates the Plivo SMS provider (HTTP Basic; the Auth ID is also a path parameter).

func (PlivoConfig) ProviderID

func (PlivoConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type PostmarkConfig

type PostmarkConfig struct {
	// ServerToken is the Postmark server token (sent as X-Postmark-Server-Token).
	ServerToken string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed as "Name <addr>".
	SenderName string
	// MessageStream is an optional message stream id (e.g. "outbound" for
	// transactional, "broadcasts" for marketing), applied to every send unless
	// overridden via Passthrough.Body["MessageStream"].
	MessageStream string
}

PostmarkConfig authenticates the Postmark email provider.

func (PostmarkConfig) ProviderID

func (PostmarkConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type ProviderCode

type ProviderCode string

ProviderCode is the normalized, cross-provider error classification surfaced on every *ConnectorError. The six values are byte-identical across the thinwrap notifications siblings (TypeScript, PHP, Go) — the notifications scope uses only the six canonical codes (no scope-extended codes).

const (
	CodeInvalidRecipient    ProviderCode = "invalid_recipient"
	CodeRateLimited         ProviderCode = "rate_limited"
	CodeAuthFailed          ProviderCode = "auth_failed"
	CodeProviderUnavailable ProviderCode = "provider_unavailable"
	CodeInvalidRequest      ProviderCode = "invalid_request"
	CodeUnknown             ProviderCode = "unknown"
)

type ProviderID

type ProviderID string

ProviderID is the stable, user-facing identifier for a notification provider. The string values are byte-identical across the thinwrap notifications siblings (TypeScript, PHP, Go).

const (
	// Email providers.
	Ses        ProviderID = "ses"
	Resend     ProviderID = "resend"
	Mailgun    ProviderID = "mailgun"
	Sendgrid   ProviderID = "sendgrid"
	Postmark   ProviderID = "postmark"
	Mailersend ProviderID = "mailersend"
	Mailtrap   ProviderID = "mailtrap"
	Brevo      ProviderID = "brevo"
	Sparkpost  ProviderID = "sparkpost"
	Scaleway   ProviderID = "scaleway"

	// SMS providers.
	Vonage      ProviderID = "vonage"
	Twilio      ProviderID = "twilio"
	Plivo       ProviderID = "plivo"
	Sns         ProviderID = "sns"
	Sinch       ProviderID = "sinch"
	Telnyx      ProviderID = "telnyx"
	Infobip     ProviderID = "infobip"
	Messagebird ProviderID = "messagebird"
	Textmagic   ProviderID = "textmagic"
	D7Networks  ProviderID = "d7networks"

	// Push providers.
	Fcm         ProviderID = "fcm"
	Expo        ProviderID = "expo"
	Apns        ProviderID = "apns"
	OneSignal   ProviderID = "one-signal"
	PusherBeams ProviderID = "pusher-beams"
	Wonderpush  ProviderID = "wonderpush"

	// Chat providers.
	Telegram         ProviderID = "telegram"
	Slack            ProviderID = "slack"
	WhatsAppBusiness ProviderID = "whatsapp-business"
	Discord          ProviderID = "discord"
	MsTeams          ProviderID = "ms-teams"
	GoogleChat       ProviderID = "google-chat"
	Mattermost       ProviderID = "mattermost"
	RocketChat       ProviderID = "rocket-chat"
	Line             ProviderID = "line"
)

type Push

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

Push is the unified push facade. Construct it with NewPush.

func NewPush

func NewPush(cfg PushConfig, opts ...Option) *Push

NewPush builds a push facade for the provider selected by cfg's type.

func (*Push) ChannelType

func (p *Push) ChannelType() ChannelType

ChannelType is always ChannelPush.

func (*Push) ProviderID

func (p *Push) ProviderID() ProviderID

ProviderID returns the provider this facade dispatches to.

func (*Push) Send

func (p *Push) Send(ctx context.Context, in PushSendInput) (*PushSendResult, error)

Send delivers a push notification through the configured provider.

type PushConfig

type PushConfig interface {
	ProviderID() ProviderID
	// contains filtered or unexported methods
}

PushConfig is implemented by every push-provider config value.

type PushSendInput

type PushSendInput struct {
	// To is a single device token / recipient. Topic / multi-target delivery
	// flows through Passthrough.
	To          string
	Title       string
	Body        string
	Data        map[string]string
	Passthrough *Passthrough
}

PushSendInput is the normalized, 4-field push baseline (To, Title, Body, Data) per the ≥90% baseline-coverage rule. Sub-baseline fields (badge, sound, ttl, platform blocks, …) ride in Passthrough or on the provider Config.

type PushSendResult

type PushSendResult struct {
	Success           bool
	Status            SendStatus
	ProviderMessageID string
	Raw               any
}

PushSendResult is the normalized result of a push send.

type PusherBeamsConfig

type PusherBeamsConfig struct {
	// InstanceID is the Beams instance ID (from the Beams dashboard). It is
	// part of both the request host and path.
	InstanceID string
	// SecretKey is the server-side secret key (from the Beams dashboard). Sent
	// verbatim as "Authorization: Bearer <SecretKey>" — no signing, no caching.
	SecretKey string
}

PusherBeamsConfig authenticates the Pusher Beams push provider.

func (PusherBeamsConfig) ProviderID

func (PusherBeamsConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type ResendConfig

type ResendConfig struct {
	// APIKey is the Resend API key (Bearer secret; typically starts with "re_").
	APIKey string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed as "Name <addr>".
	SenderName string
}

ResendConfig authenticates the Resend email provider.

func (ResendConfig) ProviderID

func (ResendConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type RocketChatConfig

type RocketChatConfig struct {
	// WebhookURL is the Rocket.Chat Incoming Webhook URL (Integrations -> New ->
	// Incoming Webhook) — it IS the credential.
	WebhookURL string
}

RocketChatConfig authenticates the Rocket.Chat provider via an Incoming Webhook URL. Webhook-URL-as-auth: the WebhookURL itself IS the credential — leaking it grants posting authority on the destination channel.

func (RocketChatConfig) ProviderID

func (RocketChatConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type ScalewayConfig

type ScalewayConfig struct {
	// SecretKey is the Scaleway IAM API secret key (sent verbatim in the
	// X-Auth-Token header).
	SecretKey string
	// ProjectID is the Scaleway project id. REQUIRED — written to every send as
	// the project_id wire field.
	ProjectID string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed into the from.name field.
	SenderName string
	// Region is an EU region interpolated into the endpoint PATH (not a host
	// swap). Defaults to "fr-par"; valid values: fr-par, nl-ams, pl-waw.
	Region string
}

ScalewayConfig authenticates the Scaleway Transactional Email (TEM) provider.

func (ScalewayConfig) ProviderID

func (ScalewayConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type SendStatus

type SendStatus string

SendStatus is the normalized delivery status on every *SendResult. Values are byte-identical across the thinwrap notifications siblings.

const (
	StatusSent       SendStatus = "sent"
	StatusQueued     SendStatus = "queued"
	StatusRejected   SendStatus = "rejected"
	StatusSuppressed SendStatus = "suppressed"
	StatusUnknown    SendStatus = "unknown"
)

type SendgridConfig

type SendgridConfig struct {
	// APIKey is the SendGrid API key (Bearer secret; typically starts with "SG.").
	APIKey string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed alongside From.
	SenderName string
}

SendgridConfig authenticates the SendGrid email provider.

func (SendgridConfig) ProviderID

func (SendgridConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type SesConfig

type SesConfig struct {
	// Region is the AWS region (e.g. "us-east-1"); forms the host
	// email.<Region>.amazonaws.com and the SigV4 credential scope.
	Region string
	// AccessKeyID is the AWS access key id.
	AccessKeyID string
	// SecretAccessKey is the AWS secret access key (SigV4 signing secret).
	SecretAccessKey string
	// SessionToken is an optional STS session token (temporary credentials).
	SessionToken string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional display name composed as "Name <addr>".
	SenderName string
	// ConfigurationSetName is the default SES configuration set applied to every
	// send. A per-send override rides in Passthrough.Body["configurationSetName"].
	ConfigurationSetName string
}

SesConfig authenticates the Amazon SES (v2) email provider. The region comes exclusively from Region — no environment inference and no shared-credentials-file fallback.

func (SesConfig) ProviderID

func (SesConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type SinchConfig

type SinchConfig struct {
	// ServicePlanID is the Sinch Service Plan ID (URL path parameter).
	ServicePlanID string
	// APIToken is the Sinch Bearer token.
	APIToken string
	// From is the default sender; per-send From overrides it.
	From string
	// Region is the regional cluster selector ("us" or "eu"); defaults to "us".
	Region string
}

SinchConfig authenticates the Sinch SMS provider. Sinch's xms API is region-clustered: the base URL is https://<region>.sms.api.sinch.com/... with the Service Plan ID as a path parameter.

func (SinchConfig) ProviderID

func (SinchConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type SlackConfig

type SlackConfig struct {
	// WebhookURL is a Slack Incoming Webhook URL — it IS the credential.
	WebhookURL string
}

SlackConfig authenticates the Slack Incoming Webhook chat provider. This is webhook-URL-as-auth: the URL itself IS the credential and targets the channel directly, so ChatSendInput.To is unused.

func (SlackConfig) ProviderID

func (SlackConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type Sms

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

Sms is the unified SMS facade. Construct it with NewSms.

func NewSms

func NewSms(cfg SmsConfig, opts ...Option) *Sms

NewSms builds an SMS facade for the provider selected by cfg's type.

func (*Sms) ChannelType

func (s *Sms) ChannelType() ChannelType

ChannelType is always ChannelSMS.

func (*Sms) ProviderID

func (s *Sms) ProviderID() ProviderID

ProviderID returns the provider this facade dispatches to.

func (*Sms) Send

func (s *Sms) Send(ctx context.Context, in SmsSendInput) (*SmsSendResult, error)

Send delivers an SMS through the configured provider.

type SmsConfig

type SmsConfig interface {
	ProviderID() ProviderID
	// contains filtered or unexported methods
}

SmsConfig is implemented by every SMS-provider config value.

type SmsSendInput

type SmsSendInput struct {
	From        string
	To          string
	Body        string
	Passthrough *Passthrough
}

SmsSendInput is the normalized, ≥90%-baseline SMS payload. Provider-specific fields (Twilio MediaUrl / MessagingServiceSid, …) ride in Passthrough or on the provider Config.

type SmsSendResult

type SmsSendResult struct {
	Success           bool
	Status            SendStatus
	ProviderMessageID string
	Raw               any
}

SmsSendResult is the normalized result of an SMS send.

type SnsConfig

type SnsConfig struct {
	// Region is an SMS-eligible AWS region (e.g. "us-east-1"). Required; there
	// is no environment inference.
	Region string
	// AccessKeyID is the AWS access key id (SigV4 credential).
	AccessKeyID string
	// SecretAccessKey is the AWS secret access key (SigV4 signing secret).
	SecretAccessKey string
	// SessionToken is an optional STS-issued temporary session token; when set
	// it is signed and sent as X-Amz-Security-Token.
	SessionToken string
}

SnsConfig authenticates the AWS SNS SMS provider. SNS SMS publishing is region-scoped and signed with AWS Signature V4 (hand-rolled on the standard library via signAWSv4 — no third-party SDK).

func (SnsConfig) ProviderID

func (SnsConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type SparkPostConfig

type SparkPostConfig struct {
	// APIKey is the SparkPost API key, sent verbatim in the Authorization header
	// (SparkPost is an outlier — NO "Bearer " prefix).
	APIKey string
	// From is the default sender email address (per-send From overrides it).
	From string
	// SenderName is an optional sender display name applied alongside From.
	SenderName string
	// Region selects the endpoint host: "" or "us" → api.sparkpost.com; "eu" →
	// api.eu.sparkpost.com.
	Region string
}

SparkPostConfig authenticates the SparkPost email provider.

func (SparkPostConfig) ProviderID

func (SparkPostConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type TelegramConfig

type TelegramConfig struct {
	// BotToken is the bot token issued by @BotFather; it is embedded in the
	// request URL path (POST /bot<token>/sendMessage).
	BotToken string
}

TelegramConfig authenticates the Telegram Bot API chat provider.

func (TelegramConfig) ProviderID

func (TelegramConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type TelnyxConfig

type TelnyxConfig struct {
	// APIKey is the Telnyx API key (Bearer secret).
	APIKey string
	// From is the default sender (E.164, short code, or alphanumeric sender id);
	// per-send From overrides it.
	From string
}

TelnyxConfig authenticates the Telnyx SMS provider (Bearer API key).

func (TelnyxConfig) ProviderID

func (TelnyxConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type TextmagicConfig

type TextmagicConfig struct {
	// Username is the Textmagic account username (sent as X-TM-Username).
	Username string
	// APIKey is the Textmagic API key (sent as X-TM-Key).
	APIKey string
	// From is the default sender (alphanumeric or E.164); per-send From overrides it.
	From string
}

TextmagicConfig authenticates the Textmagic SMS provider. Textmagic auth is a two-header pair (X-TM-Username + X-TM-Key), distinct from every other wrapped SMS provider's single-header / Basic / Bearer scheme.

func (TextmagicConfig) ProviderID

func (TextmagicConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type TokenCacheHook

type TokenCacheHook interface {
	// Get returns the cached token and its absolute expiry. ok is false when
	// there is no entry (or the caller should treat it as a miss).
	Get(key string) (token string, expiresAt time.Time, ok bool)
	// Set stores a token under key with an absolute expiry.
	Set(key, token string, expiresAt time.Time)
}

TokenCacheHook is an optional consumer-provided cache for the FCM and APNs auth tokens. The wrapper holds no token state: connectors sign fresh on every Send by default. A consumer wanting to amortize signing cost supplies a TokenCacheHook on the provider config.

On a vendor 401/403 the wrapper does NOT auto-evict — the consumer's cache layer owns eviction policy.

type TwilioConfig

type TwilioConfig struct {
	AccountSid string
	AuthToken  string
	// From is the default sender (E.164 or short code); per-send From overrides it.
	From string
	// Region selects a regional cluster; omit for the canonical us1 endpoint.
	Region string
}

TwilioConfig authenticates the Twilio SMS provider.

func (TwilioConfig) ProviderID

func (TwilioConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type VonageConfig

type VonageConfig struct {
	// APIKey is the Vonage api_key — sent as a form field, not a header.
	APIKey string
	// APISecret is the Vonage api_secret — sent as a form field, not a header.
	APISecret string
	// From is the default sender (E.164 or alphanumeric); per-send From overrides it.
	From string
}

VonageConfig authenticates the Vonage (Nexmo) SMS provider. Vonage is one of the few wrapped providers that authenticates via form fields (api_key / api_secret) instead of an HTTP header.

func (VonageConfig) ProviderID

func (VonageConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type WhatsAppBusinessConfig

type WhatsAppBusinessConfig struct {
	// AccessToken is the Meta Graph API system-user access token (Bearer credential).
	AccessToken string
	// PhoneNumberID is the numeric phone-number ID from Meta Business Manager —
	// NOT the phone number itself.
	PhoneNumberID string
	// GraphAPIVersion is the Graph API version segment embedded in the request
	// URL (e.g. "v21.0"). Empty defaults to "v21.0"; override to track Meta's
	// ~2-year deprecation window.
	GraphAPIVersion string
}

WhatsAppBusinessConfig authenticates the WhatsApp Business (Meta Cloud API) chat provider. Token-auth: AccessToken is a long-lived Meta Business Manager system-user token — leaking it grants posting authority on the business phone number.

func (WhatsAppBusinessConfig) ProviderID

func (WhatsAppBusinessConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

type WonderPushConfig

type WonderPushConfig struct {
	// AccessToken is the Management API access token (from the WonderPush
	// dashboard). Sent as "Authorization: Bearer <AccessToken>".
	AccessToken string
	// ApplicationID is an optional WonderPush application id. When set it is
	// forwarded into the request body as applicationId.
	ApplicationID string
}

WonderPushConfig authenticates the WonderPush push provider.

func (WonderPushConfig) ProviderID

func (WonderPushConfig) ProviderID() ProviderID

ProviderID reports the provider this config selects.

Jump to

Keyboard shortcuts

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