Docs
Tutorial

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

MailgunResend
Free tier100 emails/day (Flex)3,000 emails/month
Developer experienceGoodExcellent
Webhook support
Domain verificationDNS recordsDNS records
Best forHigh volume, enterpriseDevelopers, modern DX

Set your choice in .env:

EMAIL_PROVIDER="resend"   # resend | mailgun

Option A — Resend

Create a Resend Account

  1. Go to resend.com and sign up.
  2. After signing in, navigate to API Keys in the left sidebar.
  3. Click Create API Key, give it a name (e.g. tikship-production), and set permissions to Sending access.
  4. Copy the key — it starts with re_. You will not be able to see it again.

Add and Verify Your Domain

  1. In the Resend dashboard go to Domains → Add Domain.
  2. Enter your domain (e.g. tikship.top).
  3. Resend will show you a set of DNS records to add. Add all of them to your DNS provider:
TypeNameValue
TXTresend._domainkeyDKIM public key
TXT@ or sendSPF record (v=spf1 include:amazonses.com ~all)
CNAMEem.yourdomain.combounce tracking
  1. 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:

AddressBehaviour
delivered@resend.devSimulates successful delivery
bounced@resend.devSimulates a bounce
complained@resend.devSimulates 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 response

Option B — Mailgun

Create a Mailgun Account

  1. Go to mailgun.com and sign up.
  2. Choose the Flex (pay-as-you-go) plan to start free.
  3. 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.

  1. Go to Sending → Domains → Add new domain.
  2. Enter your domain (e.g. mg.tikship.top).
  3. Mailgun shows the DNS records you must add:

SPF record

Type:  TXT
Name:  mg.tikship.top
Value: v=spf1 include:mailgun.org ~all

DKIM 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.org

Tracking CNAME (optional, for open/click tracking)

Type:  CNAME
Name:  email.mg.tikship.top
Value: mailgun.org
  1. 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

  1. Go to Account → API Security → Mailgun API Keys.
  2. 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>"
VariableDescription
MAILGUN_API_KEYPrivate API key from Mailgun dashboard
MAILGUN_DOMAINThe sending domain you verified in step 2
MAILGUN_FROM_EMAILSender 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.

  1. Go to Sending → Domains — note the sandbox<hash>.mailgun.org domain.
  2. Under Authorized Recipients, add your own email.
  3. Set MAILGUN_DOMAIN to 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 panel

Check 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/:

FileTemplates inside
templates-verification.tsverificationCodePlainText
templates-order.tsorderSuccessPlainText, 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.com

Mailgun 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.org

Production Checklist

  • Domain verified with chosen provider (SPF + DKIM green)
  • EMAIL_PROVIDER set correctly in production .env
  • *_FROM_EMAIL uses 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.

Email Service Integration (Mailgun & Resend) | Tikship