Email for frontend developers
Send email straight from your frontend.Pay for your peaks, not all year.
Connect the email provider you already use, build HTML templates with typed parameters and call send() from the browser. When a seasonal rush outgrows your quota, nothing is dropped – messages wait in a hold queue and go out the moment you top up.
Free plan with 300 emails a month. No card required.
contact.js
import { init, sendForm } from '@frontmail/browser';
init({ publicKey: 'pk_live_2Jx8…' });
const form = document.querySelector('#contact');
form.addEventListener('submit', async (event) => {
event.preventDefault();
const { status } = await sendForm('svc_default', 'tpl_contact', form);
// 'queued' – or 'held' when you're out of credits. Never dropped.
});ContactForm.tsx
import { FrontmailProvider, useSendEmail } from '@frontmail/react';
function ContactForm() {
const { send, status } = useSendEmail('svc_default', 'tpl_contact');
return (
<form
onSubmit={(event) => {
event.preventDefault();
const { email, message } = Object.fromEntries(new FormData(event.currentTarget));
send({ email, message });
}}
>
<input name="email" type="email" required />
<textarea name="message" required />
<button disabled={status === 'sending'}>Send</button>
</form>
);
}
const options = { publicKey: 'pk_live_2Jx8…' };
// <FrontmailProvider options={options}><ContactForm /></FrontmailProvider>ContactForm.vue
<script setup lang="ts">
import { reactive } from 'vue';
import { useSendEmail } from '@frontmail/vue';
const { send, status } = useSendEmail('svc_default', 'tpl_contact');
const form = reactive({ email: '', message: '' });
</script>
<template>
<form @submit.prevent="send(form)">
<input v-model="form.email" type="email" required />
<textarea v-model="form.message" required />
<button :disabled="status === 'sending'">Send</button>
</form>
</template>ContactForm.svelte
<script lang="ts">
import { createFrontmail, frontmailForm } from '@frontmail/svelte';
const frontmail = createFrontmail({ publicKey: 'pk_live_2Jx8…' });
const { status } = frontmail;
</script>
<form use:frontmailForm={{ serviceId: 'svc_default', templateId: 'tpl_contact', frontmail }}>
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button disabled={$status === 'sending'}>Send</button>
</form>order-confirmation.ts
import { Frontmail } from '@frontmail/node';
const frontmail = new Frontmail({ privateKey: process.env.FRONTMAIL_PRIVATE_KEY });
await frontmail.send({
serviceId: 'svc_transactional',
templateId: 'tpl_order_confirmation',
params: {
customer_name: 'Jana',
order_id: 'A-1042',
items: [{ title: 'Ceramic mug', qty: 2 }],
},
});How it works
From form to inbox in three steps
Connect a service
Plug in SMTP, Gmail, Outlook or a transactional provider like Mailgun, Postmark or SES. Credentials are encrypted and checked daily.
Design an HTML template
Write full HTML with Handlebars, define typed parameters and watch a live preview on desktop, mobile, light and dark.
Send from your code
Call send() with your public key from any framework – or from your server with a private key. Retries and idempotency are built in.
No lost email
Running out of credits never means losing a message
Quotas run out and OAuth tokens expire. Frontmail treats both as a reason to wait, never a reason to drop an email.
frontmail.send()- Credits available?
- YesQueued and sentQuota first, then the pack that expires soonest.
- NoOverdraft buffer+10% of your monthly quota on paid plans, billed on the next invoice.
- Held, not droppedThe API answers 202 with status "held". Messages wait up to 3 days on Free or 14 days on paid plans.
- Released automaticallyA pack purchase, auto top-up, upgrade or new billing period sends held mail, oldest first.
- Provider rejects your credentials?
- Backup service takes overTemplates can name a fallback service for auth or permanent errors.
- Held until fixedWithout a backup, messages are held and resent once the service is healthy again.
Hold queue
No credit? The request is accepted and stored. You get an email before anything expires, and you can release or discard held messages in bulk.
Overdraft buffer
Paid plans get 10% of their monthly quota as a buffer, so a slightly busier month just keeps sending.
Auto top-up
Pick a pack and a threshold – or top up on the first held message. Spending caps keep surprises off your card.
Health checks & failover
Every service is checked daily and on every auth error. Broken credentials trigger an alert, a dashboard banner and the backup service.
Usage alerts at 50, 80 and 100% and on your first held message.
Credit packs
Keep a small plan. Buy packs for the busy months.
Packs are one-off purchases valid for 3–12 months and work on every plan, Free included. Enter your expected volume per month and we will find the cheapest mix.
5,000 emails
valid 3 months
$10
$2 per 1,000
Buy this pack25,000 emails
valid 6 months
$39
$1.56 per 1,000
Buy this pack100,000 emails
valid 12 months
$129
$1.29 per 1,000
Buy this pack
Our recommendation
Free plan
Packs to buy
- January: 1× 5,000-email pack$10
- March: 1× 25,000-email pack$39
- July: 1× 25,000-email pack$39
- December: 1× 25,000-email pack$39
- Plan
- $0
- Packs
- $127
- Total for the year
- $127
A plan that covers your 25,000-email peak all year (Business 50k) would cost $468. You save $341 (73%).
Start freeFeatures
Everything a contact form needs – and a lot more
HTML templates with live preview
Full HTML, Handlebars conditions and loops, typed parameters with validation, versions with diff and rollback.
Your provider, your reputation
SMTP, Gmail, Outlook, Mailgun, SES, Postmark, SendGrid, Mailjet, Brevo and Resend, with a backup service per template.
Spam protection built in
Cloudflare Turnstile on all your forms in one click, domain allowlist, per-IP and per-key rate limits, headless-browser blocking and anomaly alerts.
Searchable history
Rendered content, parameters and an event timeline for every message. Resend in one click, export to CSV.
Delivery tracking
Delivered, bounced, complained, opened and clicked – straight from your provider’s webhooks, with hard bounces suppressed automatically.
Teams, roles and MFA
Invite teammates as owner, admin, developer, viewer or billing. TOTP, backup codes and an audit log for sensitive actions.
SDKs with typed parameters
Vanilla JS, React, React Native, Vue, Svelte and Node. Run npx frontmail types and your editor knows every template parameter.
Errors that explain themselves
One error format with a code, a message and a link to the docs. SDKs retry network errors and 5xx without sending twice.
Providers
Works with the email service you already have
Personal mailboxes for small sites, transactional providers for scale – with a step-by-step guide for each one in the docs.
- SMTPSeznam, Zoho, your own server…SMTP
- GmailOAuth
- Outlook / Microsoft 365OAuth
- Amazon SESAPI
- MailgunEU / USAPI
- PostmarkAPI
- SendGridAPI
- MailjetAPI
- BrevoAPI
- ResendAPI
Moving from EmailJS
Change one import
The @frontmail/emailjs-compat package exposes the same functions as @emailjs/browser. Recreate your service and template in Frontmail, swap the import, keep your code.
- 1Connect your email service and import your template HTML.
- 2Replace the package import and your keys.
- 3Ship. Optionally switch to @frontmail/browser later for typed parameters.
- import emailjs from '@emailjs/browser';
+ import emailjs from '@frontmail/emailjs-compat';
emailjs.init({ publicKey: 'YOUR_PUBLIC_KEY' });
emailjs
.sendForm('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', '#contact-form')
.then((response) => console.log('SUCCESS', response.status));How the two services compare
| Situation | EmailJS* | Frontmail |
|---|---|---|
| Monthly quota used up | Further requests are not sent until the quota resets or the plan changes. | Requests are accepted and held for up to 14 days (3 days on Free), then sent automatically after a top-up. |
| REST API rate limit | 1 request per second. | 2–50 requests per second depending on plan, plus a batch endpoint. |
| Seasonal peak | Move to a higher plan. | Keep your plan and buy a credit pack valid for 3–12 months. |
* EmailJS information as of September 2026, per emailjs.com/pricing. EmailJS is a trademark of its owner; Frontmail is not affiliated with EmailJS.
Pricing
Simple plans. Packs for the peaks.
Start free, upgrade when you need more. Yearly billing saves 20%.
Free
300 emails / month
$0/ month
Free forever
Start free- 3 templates
- No attachments
- History: 7 days
- 1 user
- 2 requests / second
- Hold queue: 3 days
- Domain allowlist & Turnstile
- “Sent via Frontmail” footer
Starter
3,000 emails / month
$7/ month
$84 billed yearly
Choose Starter- 10 templates
- Attachments up to 1 MB per request
- History: 30 days
- 1 user
- 5 requests / second
- Hold queue: 14 days
- 10% overdraft buffer
- Domain allowlist & Turnstile
- Most popular
Pro
10,000 emails / month
$15/ month
$180 billed yearly
Choose Pro- Unlimited templates
- Attachments up to 5 MB per request
- History: 90 days
- 5 users
- 20 requests / second
- Hold queue: 14 days
- 10% overdraft buffer
- Domain allowlist & Turnstile
Business
50,000 emails / month
$39/ month
$468 billed yearly
Choose Business 50k- Unlimited templates
- Attachments up to 25 MB per request
- History: 180 days
- Unlimited users
- 50 requests / second
- Hold queue: 14 days
- 10% overdraft buffer
- Domain allowlist & Turnstile
Business tiers
Credit packs
One-off purchase, available on every plan. Used after your monthly quota, soonest-expiring first.
Compare all plans
| Plan | Free | Starter | Pro | Business 50k | Business 100k | Business 200k |
|---|---|---|---|---|---|---|
| Price / month | $0 | $7 | $15 | $39 | $71 | $127 |
| Emails / month | 300 | 3,000 | 10,000 | 50,000 | 100,000 | 200,000 |
| Templates | 3 | 10 | Unlimited | Unlimited | Unlimited | Unlimited |
| Attachments | – | 1 MB | 5 MB | 25 MB | 25 MB | 25 MB |
| History | 7 days | 30 days | 90 days | 180 days | 180 days | 180 days |
| Users | 1 | 1 | 5 | Unlimited | Unlimited | Unlimited |
| Rate limit | 2 rps | 5 rps | 20 rps | 50 rps | 50 rps | 50 rps |
| Overdraft | – | 10% | 10% | 10% | 10% | 10% |
| Hold queue | 3 days | 14 days | 14 days | 14 days | 14 days | 14 days |
Prices exclude VAT. EU businesses with a valid VAT ID are invoiced under reverse charge.
Billing questions
Does unused quota roll over?
No – the monthly quota resets every billing period. Credit packs are different: they stay valid for 3, 6, or 12 months, so buy them when you need them.
What happens when I run out of credits?
Paid plans first use the overdraft buffer. After that, new messages are held (not rejected) and go out automatically once you buy a pack, auto top-up kicks in, you upgrade or a new period starts. If you prefer hard failures, switch your organization to reject mode and the API returns 402.
How is the overdraft billed?
Overdraft credits you actually used are added to your next invoice. Unused overdraft costs nothing.
Can I change or cancel my plan?
Upgrades apply immediately with a prorated charge. Downgrades and cancellations take effect at the end of the paid period. Packs you have already bought remain valid until they expire.
Which currency will I pay in?
Customers billed in the EU pay in euros, everyone else in US dollars. VAT is calculated at checkout based on your billing country and VAT ID.
How can I pay?
By card, Apple Pay or Google Pay, and SEPA Direct Debit where available. Every payment comes with an invoice you can download from the dashboard.
FAQ
Frequently asked questions
Is it safe to put a public key in my frontend?
Yes. A public key can only send with your templates, only from domains on your allowlist, and behind Turnstile and rate limits. Private keys are for servers only and never belong in browser code.
Do I need my own email provider?
Yes – Frontmail sends through a service you connect, so messages come from your domain and your reputation. A personal Gmail or Outlook account is fine for a small site.
What does the Free plan include?
300 emails a month, 3 templates, spam protection and 7 days of history. Emails include a small “Sent via Frontmail” footer. You can buy credit packs on Free too.
Where is my data stored?
In the AWS eu-central-1 region (Frankfurt, Germany). Provider credentials are encrypted with AWS KMS. Message history is kept only as long as your plan allows.
What if my Gmail or Outlook connection expires?
We check every service daily and immediately after an authentication error. You get an email and a dashboard banner; messages use your backup service or wait as held until you reconnect.
Can I send from a server too?
Yes. The Node SDK and REST API accept a private key, which skips the origin check and CAPTCHA and unlocks batch sending of up to 100 messages per request.
Ship the form today. Stop worrying about the busy season.
Free plan, no card, full API. Upgrade or add a pack whenever your traffic says so.