A lightweight, multi-tenant email delivery service built with Go and the Resend API. Designed as an internal microservice that provides templated email sending for the Sysopoly platform.
- Multi-domain support — configure multiple sending domains (e.g.,
sethabit.com,sysopoly.com) via environment variables - Template engine — built-in HTML + plain-text email templates with dynamic data injection
- Batch sending — send multiple emails in a single API call via Resend's batch endpoint
- Request tracing — automatic request ID generation and propagation for distributed tracing
- Internal auth — API key authentication for service-to-service communication
- Graceful shutdown — proper signal handling for zero-downtime deployments
- Structured logging — JSON-formatted logs via
slogwith environment-aware log levels
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Internal Svc │──POST──▶│ Email Service │──HTTP──▶│ Resend API │
│ (scheduler, │ │ (Gin + auth) │ │ │
│ auth, api) │ └──────────────────┘ └─────────────┘
└─────────────────┘
All configuration is via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
RESEND_API_KEY |
Yes | — | Resend API key for sending |
INTERNAL_API_KEY |
Yes | — | Shared secret for authenticating internal callers |
PORT |
No | 8086 |
HTTP server port |
ENVIRONMENT |
No | development |
development or production |
DEFAULT_DOMAIN |
No | sethabit |
Default domain alias when none specified |
DOMAIN_<ALIAS> |
Yes (≥1) | — | Domain config: name|from|replyto |
LOGGING_SERVICE_URL |
No | — | Remote logging endpoint |
Each domain is configured via a DOMAIN_<ALIAS> environment variable with pipe-separated values:
DOMAIN_SETHABIT="sethabit.com|SetHabit <noreply@sethabit.com>|support@sethabit.com"
DOMAIN_SYSOPOLY="sysopoly.com|Sysopoly <noreply@sysopoly.com>|"All endpoints (except /health) require the X-Internal-API-Key header.
Health check — returns 200 OK with service status.
Send a single email.
{
"domain": "sethabit",
"to": ["user@example.com"],
"subject": "Welcome!",
"template": "welcome",
"template_data": { "name": "Alice" },
"tags": { "campaign": "onboarding" }
}Send multiple emails in one request.
{
"messages": [
{ "to": ["a@example.com"], "subject": "Hello", "html": "<p>Hi</p>" },
{ "to": ["b@example.com"], "subject": "Hello", "template": "welcome" }
]
}| Template | Purpose |
|---|---|
welcome |
New user onboarding |
gdpr_report |
GDPR data export notification |
account_deleted |
Account deletion confirmation |
marketing_campaign |
Generic marketing email with CTA |
# Run tests
make test
# Run with race detector
make test-race
# Build binary
make build
# Run locally (requires env vars)
make run
# Docker
docker compose up --buildThe service is deployed as a container via GitHub Actions. The workflow builds, tests, and pushes to GHCR on every push to master.
docker pull ghcr.io/sysopoly/sysopoly-email-service:latestMIT