github.com/thinwrap/notifications-go

thinwrap/notifications-go for Go — unified facade for email, SMS, push, and chat across 35 providers. One constructor per channel; switch vendor by changing the config struct. Truly zero deps (net/http only), stateless, BYO *http.Client.

Go constraints

Runtime dependencies
Zero runtime dependencies
BYO HTTP client
injectable *http.Client via the WithHTTPClient option
Provenance
Go module checksum database (sum.golang.org)
Minimum runtime
Go 1.18+
  • Truly zero dependencies — the standard library net/http is the only HTTP surface, and AWS SigV4 (Bedrock) / JWT signing (FCM, APNs) are hand-rolled on crypto/*.
  • Distributed as a Go module with no build step or codegen: the git tag IS the release. The public module proxy serves it directly and integrity is verified through the Go checksum database (sum.golang.org) and your go.sum — there is no separately signed release artifact, and none is needed for `go get`.
  • The default *http.Client never follows redirects — a 3xx surfaces as an error rather than silently re-sending auth headers to another host.

30-second install

go get github.com/thinwrap/notifications-go

2-minute end-to-end example

One constructor per channel; the config struct selects the provider. A provider that doesn't serve a channel is unrepresentable at compile time (e.g. TwilioConfig does not satisfy EmailConfig).

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

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)

Switch vendor by changing the config struct — swap toResendConfig, PostmarkConfig,SesConfig, and so on. The Send(ctx, in) call and the EmailSendResult stay identical. Same pattern forNewSms, NewPush, and NewChat.

Supported providers

35 providers ship with github.com/thinwrap/notifications-go@1.0.0.
ProviderChannelAuthREADME
sendgrid SendGridemailbearerdocs
ses AWS SESemailaws-sig-v4docs
mailgun Mailgunemailbasicdocs
postmark Postmarkemailheaderdocs
resend Resendemailbearerdocs
brevo Brevo (Sendinblue)emailheaderdocs
mailersend MailerSendemailbearerdocs
mailtrap Mailtrapemailbearerdocs
sparkpost SparkPostemailheaderdocs
scaleway Scalewayemailheaderdocs
twilio Twiliosmsbasicdocs
vonage Vonagesmsquerydocs
plivo Plivosmsbasicdocs
sns AWS SNSsmsaws-sig-v4docs
messagebird MessageBird (Bird)smsheaderdocs
infobip Infobipsmsheaderdocs
telnyx Telnyxsmsbearerdocs
sinch Sinchsmsbearerdocs
textmagic Textmagicsmsheaderdocs
d7networks D7 Networkssmsbearerdocs
fcm Firebase Cloud Messagingpushrs256-jwtdocs
apns Apple Push Notificationspushes256-jwtdocs
expo Expo Pushpushbearerdocs
one-signal OneSignalpushheaderdocs
pusher-beams Pusher Beamspushbearerdocs
wonderpush WonderPushpushbearerdocs
slack Slackchatwebhook-urldocs
discord Discordchatwebhook-urldocs
ms-teams Microsoft Teamschatwebhook-urldocs
telegram Telegramchatwebhook-urldocs
whatsapp-business WhatsApp Businesschatbearerdocs
line LINEchatbearerdocs
google-chat Google Chatchatwebhook-urldocs
rocket-chat Rocket.Chatchatwebhook-urldocs
mattermost Mattermostchatwebhook-urldocs

Migration paths

Zero-dependency signing

No vendor SDKs and no third-party dependencies — AWS SigV4 (SES/SNS) and JWT signing (FCM RS256, APNs ES256) are hand-rolled on crypto/*. The wrapper is stateless: FCM/APNs sign a fresh token on everySend unless you supply a TokenCacheHook on the config.

Passthrough escape hatch

Only fields ≥90% of a channel's providers support are first-class on the input. Provider-specific fields flow through Passthrough(Body deep-merged, Headers/Queryshallow-merged; consumer values win):

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
    },
})

Bring your own *http.Client

Inject any client satisfyinginterface{ Do(*http.Request) (*http.Response, error) }via WithHTTPClient. The default client never follows redirects. There is deliberately no top-level RetryAfterSeconds— the raw Retry-After header rides in Cause and retry is consumer policy.

Cross-language siblings

The TypeScript (/packages/notifications-ts) and PHP (/packages/notifications-php) variants ship the same facade shape and provider set — cross-language parity is a design goal, not a hard release gate.

Read more