Skip to content

Authentication Model

mailiam uses a sophisticated multi-layer authentication model that provides both maximum security and developer convenience. Unlike traditional APIs that require API keys for everything, mailiam intelligently adapts authentication based on your use case.

mailiam provides three types of API keys, each designed for specific use cases:

Full account access - Complete control over your mailiam account.

Use for:

  • CLI operations (mailiam push, mailiam pull)
  • Domain management
  • API key management
  • Billing and account settings
  • All administrative operations

Security level: 🔴 Critical - Never expose client-side

Example:

mlm_sk_admin_a1b2c3d4e5f6g7h8...

Storage:

  • Store in .env files (server-side only)
  • Use in CI/CD pipelines
  • Keep in secure vaults (AWS Secrets Manager, etc.)

Limited operational access - Form submissions and email sending only.

Use for:

  • Vercel/Netlify API routes
  • Backend services
  • Programmatic form submissions
  • Email sending operations

Security level: 🟡 Moderate - Safe in server environments, not client-side

Permissions:

  • forms:send - Submit contact forms
  • email:send - Send transactional emails
  • ❌ Cannot manage domains
  • ❌ Cannot create/delete API keys
  • ❌ Cannot access billing

Example:

mlm_sk_usage_x9y8z7w6v5u4t3s2...

Storage:

  • Vercel/Netlify environment variables
  • Backend .env files
  • Container secrets

Domain-specific, safe for client-side - Limited to one domain, read-only operations.

Use for:

  • Static websites
  • Client-side JavaScript
  • Embedded forms
  • Public-facing integrations

Security level: 🟢 Safe - Can be exposed in client-side code

Characteristics:

  • Domain-scoped - Only works for ONE specific domain
  • Limited permissions - Can only submit forms for the scoped domain
  • Lower rate limits - 100 requests/hour (vs 1000 for other keys)
  • Cannot be used for - Managing settings, accessing other domains, admin operations

Example:

mlm_pk_a7f8b3e1_j4k5l6m7n8o9p0q1...
^^^^^^^^^
Domain hash - identifies the scoped domain

Usage:

// Safe to embed in client-side code!
fetch('https://api.mailiam.dev/v1/mysite.com/send', {
method: 'POST',
headers: {
'X-Public-Token': 'mlm_pk_a7f8b3e1_j4k5l6m7n8o9p0q1...'
},
body: formData
})

Choose the right authentication method for your deployment:

┌─ Need to manage domains/settings? ────────► Admin Key (server-side only)
├─ Building a backend API/service? ─────────► Usage Key (server-side)
├─ Static site with simple forms? ──────────► No API key needed!*
│ (Origin validation)
└─ Static site needing JS interactions? ────► Public Token (client-side safe)
(checking status, analytics, etc.)
* Forms work without API keys when using origin validation

Static Sites (Cloudflare Pages, GitHub Pages)

Section titled “Static Sites (Cloudflare Pages, GitHub Pages)”

Option 1: No API Key (Recommended)

Forms work automatically with origin validation:

<!-- No API key needed! -->
<form action="https://api.mailiam.dev/v1/mysite.com/send" method="POST">
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>

How it’s secure:

  1. ✅ Domain verification (DNS records)
  2. ✅ Origin header validation
  3. ✅ Browser detection (blocks curl, Postman)
  4. ✅ IP-based rate limiting
  5. ✅ Spam protection

Option 2: Public Token (For Advanced Features)

Use when you need JavaScript interactions:

// Safe for static sites
const publicToken = 'mlm_pk_a7f8b3e1_j4k5l6m7n8o9p0q1...';
// Submit form
await fetch('https://api.mailiam.dev/v1/mysite.com/send', {
headers: { 'X-Public-Token': publicToken },
method: 'POST',
body: formData
});
// Check submission status (future feature)
await fetch('https://api.mailiam.dev/v1/mysite.com/status/sub_123', {
headers: { 'X-Public-Token': publicToken }
});

Use Usage Keys in API Routes

Hide the API key server-side:

// pages/api/contact.js (Next.js example)
export default async function handler(req, res) {
const response = await fetch(
'https://api.mailiam.dev/v1/mysite.com/send',
{
method: 'POST',
headers: {
'X-Api-Key': process.env.MAILIAM_USAGE_KEY // Server-side only
},
body: JSON.stringify(req.body)
}
);
res.json(await response.json());
}

Benefits:

  • ✅ API key never exposed to client
  • ✅ Add custom validation
  • ✅ Transform data before sending
  • ✅ Implement your own rate limiting

Use Admin or Usage Keys

Depending on what operations you need:

// Full control with Admin Key
const adminKey = process.env.MAILIAM_ADMIN_KEY;
// Create domains, manage settings, etc.
await fetch('https://api.mailiam.dev/domains', {
headers: { 'Authorization': `Bearer ${adminKey}` }
});
// Just send emails with Usage Key
const usageKey = process.env.MAILIAM_USAGE_KEY;
await fetch('https://api.mailiam.dev/v1/mysite.com/send', {
headers: { 'X-Api-Key': usageKey },
method: 'POST',
body: formData
});
Terminal window
# Create admin key (default)
mailiam auth create-key --name "Production Admin"
# Create usage key for API routes
mailiam auth create-key \
--name "Vercel API Route" \
--type usage
# Create public token for a domain
mailiam auth create-key \
--name "Public Token for mysite.com" \
--type public \
--domain mysite.com
Terminal window
curl -X POST https://api.mailiam.dev/api-keys \
-H "Authorization: Bearer $MAILIAM_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Public Token for mysite.com",
"keyType": "public",
"domainScope": "mysite.com"
}'
{
"success": true,
"apiKey": {
"keyId": "key_abc123",
"name": "Public Token for mysite.com",
"keyType": "public",
"domainScope": "mysite.com",
"key": "mlm_pk_a7f8b3e1_j4k5l6m7n8o9p0q1...",
"rateLimit": 100
},
"message": "Public key created successfully. Save the key securely - it will not be shown again.",
"securityNote": "This public key is safe for client-side use but is scoped to the specified domain only."
}
ScenarioKey TypeReasoning
CLI operationsAdminNeed full account access
Vercel API routeUsageOnly need to send emails
Static website formNone or PublicOrigin validation sufficient
JavaScript interactionsPublicSafe for client-side
// ❌ NEVER DO THIS
const adminKey = 'mlm_sk_admin_...';
fetch('https://api.mailiam.dev/domains', {
headers: { 'Authorization': `Bearer ${adminKey}` }
});
// ✅ DO THIS INSTEAD
// Use a public token or no key at all for static sites
Terminal window
# Create new key
mailiam auth create-key --name "Production v2" --type usage
# Test new key
export MAILIAM_API_KEY="new_key"
mailiam test config
# Update environment variables
# Revoke old key
mailiam auth revoke-key key_old123
Terminal window
# .env.local (never commit!)
MAILIAM_ADMIN_KEY=mlm_sk_admin_...
MAILIAM_USAGE_KEY=mlm_sk_usage_...
# .env.production (Vercel/Netlify)
MAILIAM_API_KEY=mlm_sk_usage_...
Terminal window
# View usage by key
mailiam analytics api-usage --key key_abc123
# Check for suspicious activity
mailiam auth list-keys --show-last-used

If you’re upgrading from an older mailiam version with a single key type:

All legacy keys (mlm_...) are treated as admin keys for backward compatibility.

  1. Create new key types

    Terminal window
    # For Vercel/Netlify
    mailiam auth create-key --type usage --name "API Routes"
    # For static sites
    mailiam auth create-key --type public --domain mysite.com
  2. Update your applications

    • Update environment variables
    • Test thoroughly
    • Deploy changes
  3. Revoke old keys (optional)

    Terminal window
    mailiam auth revoke-key key_old123
ServiceClient-Side KeysDomain ScopingOrigin Validation
mailiam✅ Public Tokens✅ Per-domain✅ Built-in
Resend❌ No❌ Account-wide❌ Manual
SendGrid❌ No❌ Account-wide❌ Manual
EmailJS✅ Public Keys❌ Account-wide❌ Manual
Admin Keys: mlm_sk_admin_{64_hex_chars}
Usage Keys: mlm_sk_usage_{64_hex_chars}
Public Keys: mlm_pk_{8_hex_domain_hash}_{64_hex_chars}
Key TypeDefault Rate LimitCan Be Customized
Admin1000/hour✅ Yes
Usage1000/hour✅ Yes
Public100/hour⚠️ Limited
  • Keys are hashed using SHA-256 before storage
  • Only the hash is stored in the database
  • Original key is shown only once at creation
  • Cannot retrieve original key after creation

Q: Can I use a public token for multiple domains? A: No, public tokens are scoped to a single domain for security.

Q: What happens if I expose an admin key accidentally? A: Immediately revoke it using mailiam auth revoke-key and create a new one.

Q: Do I need API keys for simple contact forms? A: No! Forms work without API keys using origin validation.

Q: Can I increase the rate limit for public tokens? A: Public tokens have lower limits by design. Use a usage key in an API route for higher limits.


The key to security is using the right key for the right job. Choose wisely! 🔐