Email Service Integration (Mailgun & Resend)
Step-by-step guide to configure Mailgun or Resend as the transactional email provider in TikShip, including domain verification, DNS records, API key setup, and production checklist.
TikShip supports two transactional email providers out of the box: Mailgun and Resend. Both are routed through the same unified service layer at src/lib/email/service.ts, so you only need to configure environment variables — no code changes required.
Choosing a Provider
| Mailgun | Resend | |
|---|---|---|
| Free tier | 100 emails/day (Flex) | 3,000 emails/month |
| Developer experience | Good | Excellent |
| Webhook support | ✅ | ✅ |
| Domain verification | DNS records | DNS records |
| Best for | High volume, enterprise | Developers, modern DX |
Set your choice in .env:
EMAIL_PROVIDER="resend" # resend | mailgunOption A — Resend
Create a Resend Account
- Go to resend.com and sign up.
- After signing in, navigate to API Keys in the left sidebar.
- Click Create API Key, give it a name (e.g.
tikship-production), and set permissions to Sending access. - Copy the key — it starts with
re_. You will not be able to see it again.
Add and Verify Your Domain
- In the Resend dashboard go to Domains → Add Domain.
- Enter your domain (e.g.
tikship.top). - Resend will show you a set of DNS records to add. Add all of them to your DNS provider:
| Type | Name | Value |
|---|---|---|
| TXT | resend._domainkey | DKIM public key |
| TXT | @ or send | SPF record (v=spf1 include:amazonses.com ~all) |
| CNAME | em.yourdomain.com | bounce tracking |
- Click Verify in the Resend dashboard. DNS propagation can take up to 48 hours but usually completes within minutes.
Resend's domain dashboard shows a green ✅ next to each record once it verifies. You must verify the domain before sending to real addresses in production.
Configure Environment Variables
# .env
EMAIL_PROVIDER="resend"
RESEND_API_KEY="re_xxxxxxxxxxxxxxxxxxxx"
RESEND_FROM_EMAIL="TikShip <noreply@yourdomain.com>"RESEND_FROM_EMAIL must use a verified domain. The display name before < is optional but recommended.
Test the Integration
Resend provides magic test addresses for development — no domain verification needed:
| Address | Behaviour |
|---|---|
delivered@resend.dev | Simulates successful delivery |
bounced@resend.dev | Simulates a bounce |
complained@resend.dev | Simulates a spam complaint |
Run the dev server and trigger a registration to confirm the verification email is sent.
npm run dev
# Register a new user and check server logs for the email responseOption B — Mailgun
Create a Mailgun Account
- Go to mailgun.com and sign up.
- Choose the Flex (pay-as-you-go) plan to start free.
- Navigate to Account → API Security to find your API keys.
Mailgun has two types of keys: Private API key (server-side, keep secret) and Public validation key (email validation only). Use the Private API key for MAILGUN_API_KEY.
Add and Verify Your Sending Domain
Mailgun lets you send from either your root domain (tikship.top) or a subdomain (mg.tikship.top). Using a subdomain is recommended to isolate your transactional mail reputation.
- Go to Sending → Domains → Add new domain.
- Enter your domain (e.g.
mg.tikship.top). - Mailgun shows the DNS records you must add:
SPF record
Type: TXT
Name: mg.tikship.top
Value: v=spf1 include:mailgun.org ~allDKIM record
Type: TXT
Name: pic._domainkey.mg.tikship.top
Value: k=rsa; p=<long-public-key>MX records (for inbound routing — optional if you only send)
Type: MX Priority: 10 Value: mxa.mailgun.org
Type: MX Priority: 10 Value: mxb.mailgun.orgTracking CNAME (optional, for open/click tracking)
Type: CNAME
Name: email.mg.tikship.top
Value: mailgun.org- After adding the records, click Verify DNS Settings in the Mailgun dashboard.
You can force-check the DNS status in the Mailgun domain settings. Status badges turn green once each record is detected.
Get Your API Key
- Go to Account → API Security → Mailgun API Keys.
- Copy your Private API key (format:
key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).
Alternatively, domain-specific Sending API Keys are available under Sending → Domain Settings → Sending API keys. These are scoped to a single domain and preferred for production.
Configure Environment Variables
# .env
EMAIL_PROVIDER="mailgun"
MAILGUN_API_KEY="key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
MAILGUN_DOMAIN="mg.yourdomain.com"
MAILGUN_FROM_EMAIL="TikShip <noreply@mg.yourdomain.com>"| Variable | Description |
|---|---|
MAILGUN_API_KEY | Private API key from Mailgun dashboard |
MAILGUN_DOMAIN | The sending domain you verified in step 2 |
MAILGUN_FROM_EMAIL | Sender address — must belong to MAILGUN_DOMAIN |
If your Mailgun account is in the EU region, you need a different base URL. Currently the SDK used (mailgun.js) defaults to the US endpoint. To use EU, check the Mailgun.js docs for the url option and update src/lib/email/service.ts accordingly.
Test the Integration
Mailgun's sandbox domain (auto-created for new accounts) lets you test without adding your own domain, but delivery is restricted to Authorized Recipients only.
- Go to Sending → Domains — note the
sandbox<hash>.mailgun.orgdomain. - Under Authorized Recipients, add your own email.
- Set
MAILGUN_DOMAINto the sandbox domain temporarily and trigger a test email.
For full testing with your verified domain:
npm run dev
# Register a new user or use the Admin → Send Email panelCheck the Sending → Logs section in Mailgun to see delivery status.
How the Unified Service Works
Both providers share the same interface. All email calls in the codebase go through:
// src/lib/email/service.ts
export async function sendEmail(options: SendEmailOptions): Promise<EmailResult> {
if (EMAIL_PROVIDER === 'mailgun') {
return sendEmailWithMailgun(options)
}
return sendEmailWithResend(options)
}The EMAIL_PROVIDER environment variable is read once at module load time. Switching providers requires only a .env change and a server restart.
SendEmailOptions
interface SendEmailOptions {
to: string | string[] // recipient(s)
subject: string
html?: string // HTML body
text?: string // plain-text fallback (recommended)
from?: string // override default from address
replyTo?: string
cc?: string | string[]
bcc?: string | string[]
headers?: Record<string, string> // custom headers
}Built-in Email Templates
TikShip ships plain-text templates for all transactional events. Template source files live in src/lib/email/:
| File | Templates inside |
|---|---|
templates-verification.ts | verificationCodePlainText |
templates-order.ts | orderSuccessPlainText, orderFailedPlainText, orderExpiredPlainText, orderActivationCNPlainText, orderActivationENPlainText |
These are called automatically by the service layer — you don't need to invoke them manually.
DNS Quick-Reference
Resend DNS Records
# SPF
TXT @ "v=spf1 include:amazonses.com ~all"
# DKIM (name shown in Resend dashboard)
TXT resend._domainkey "<dkim-key-from-dashboard>"
# Bounce tracking (optional but recommended)
CNAME em.yourdomain.com feedback-smtp.us-east-1.amazonses.comMailgun DNS Records
# SPF
TXT mg.yourdomain.com "v=spf1 include:mailgun.org ~all"
# DKIM
TXT pic._domainkey.mg.yourdomain.com "k=rsa; p=<key>"
# MX (for inbound, optional)
MX mg.yourdomain.com 10 mxa.mailgun.org
MX mg.yourdomain.com 10 mxb.mailgun.orgProduction Checklist
- Domain verified with chosen provider (SPF + DKIM green)
-
EMAIL_PROVIDERset correctly in production.env -
*_FROM_EMAILuses verified domain address - API key stored as environment secret (never in git)
- Test email delivered to a real inbox before going live
- Plain-text fallback included in all custom templates
- Bounce and complaint webhooks configured (optional but recommended)
Troubleshooting
Emails not sending in development
Check the server console — sendEmail logs provider errors.
Development Workflow
Scripts, hot reload, and day-to-day development with TikShip
Payment Integration (Stripe & PayPal)
Complete step-by-step guide to setting up Stripe and PayPal in TikShip — API keys, webhook configuration, product price IDs, subscription plans, local testing with the Stripe CLI, and a production checklist.