SendGrid email provider
Twilio SendGrid provider implementing email.ProviderPort with personalisation-based batching, attachments, and automatic rate limiting.
Overview
SendGrid is a Twilio-owned ESP covering transactional sending, marketing campaigns, suppression management, and analytics on the same account. The adapter is a drop-in implementation of email.ProviderPort, enforced by a compile-time assertion in the package. Registration is by name, so swapping SendGrid for Postmark, SES, or any other email provider is a one-line bootstrap change with no call-site edits.
SendBulk uses SendGrid's personalisations API feature to fold one batch into a single request when every message shares the same subject, both body fields, and sender. That sends one API request instead of one per recipient. When subject, body, or sender differ across the batch, the provider falls back to one send per message. A single SendBulk accepts up to 1000 messages per batch, splitting larger inputs into successive batches.
The provider emits OpenTelemetry metrics for send attempts and durations through the same piko telemetry pipeline as the rest of the application. Each metric carries status and send_type labels, so the application observes SendGrid send volume and latency without extra instrumentation. All public methods are safe for concurrent use.
Requirements
- A Twilio SendGrid account with API access. Generate an API key in the SendGrid dashboard (Settings -> API Keys).
- A verified sender identity (single sender or domain authentication) matching
FromEmail. - Network egress to
api.sendgrid.com.
Configuration
import (
"context"
"os"
"piko.sh/piko/wdk/email/email_provider_sendgrid"
)
provider, err := email_provider_sendgrid.NewSendGridProvider(ctx, email_provider_sendgrid.SendGridProviderArgs{
APIKey: os.Getenv("SENDGRID_API_KEY"), // required
FromEmail: "[email protected]", // required; must be a verified sender
})
if err != nil {
return err
}
NewSendGridProvider validates that APIKey and FromEmail are non-empty and returns an error otherwise, so a misconfigured provider fails at construction, not at the first send. The provider is pure Go with no build tags or CGO, so it runs in any mode, including interpreted dev-i.
The constructor builds its own SendGrid HTTP client from the API key. There is no client override, and the only configurable knob is the rate limiter, which is not adjustable through an exported option.
Rate limiting
The provider applies piko's shared email rate limiter, the same token bucket machinery every email adapter uses. The default caps SendGrid calls at 100 per second with a burst of 200. The bucket is in-memory and per provider instance, not shared across processes. A horizontally scaled deployment of N instances gets N independent buckets, so the effective cap is N times 100 per second.
Bootstrap
ssr := piko.New(
piko.WithEmailProvider("sendgrid", provider),
piko.WithDefaultEmailProvider("sendgrid"),
)
WithDefaultEmailProvider selects the provider used when a send names none. After bootstrap, send through the email service, not by calling the provider directly. Build a message with the default service and dispatch it.
builder, err := email.NewEmailBuilderFromDefault()
if err != nil {
return err
}
err = builder.
To("[email protected]").
Subject("Welcome").
BodyHTML("<p>Hi!</p>").
Do(ctx)
Call .Provider("sendgrid") on the builder to target a named provider instead of the default. This is the hook for per-tenant or per-subuser routing.
Recipient visibility
A single Send with multiple To addresses places them all in one personalization. Every recipient sees the others in the To header. Use Cc and Bcc, or one send per recipient, when addresses must stay private.
Tradeoffs
Combining transactional and marketing on one domain is a reputation risk. A marketing-list problem can degrade transactional deliverability. Teams sending high volume split senders by subdomain and isolate reputation with SendGrid subuser accounts. The provider stays out of that decision. Register one named email.ProviderPort per subuser with WithEmailProvider, then choose it per send with .Provider(name).
See also
Other email providers:
- Postmark, transactional-only, fast delivery.
- SES, cheapest at scale if already on AWS.
- Mailgun, strong EU presence, flexible routing rules.
- Mailchimp Transactional, the former Mandrill.
- SMTP, generic backend for self-hosted or vendor-neutral relays.
- Gmail, Gmail SMTP with app passwords (dev / low-volume).
Framework docs:
- Email API reference, every type and method on the email service.
External:
- SendGrid API documentation, authoritative reference.
- Sender authentication guide, DKIM/SPF setup that affects deliverability.