# About Email Fast > Email Fast is an independent email platform built on one conviction: an email company should be able to prove what it claims. Who we are and how we built it. ## The premise The email industry runs on adjectives. "Best-in-class deliverability." "Bank-grade security." "Trusted by thousands." Almost none of it is checkable, most platforms rent the infrastructure they're selling, and every security page leans on somebody else's certificate. Email Fast was built to be the other kind of email company: Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. Every delivery can produce evidence — delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. Customers who want to go further can hold their own keys, with a revoke that locks us out too. And this website itself runs on the same principle: every factual claim in our copy is checked against a claims ledger at build time, so overstating what we've built is literally a build error. ## How it was built The platform is the work of a small, focused team using modern engineering practice to an unusual degree of rigor: 141 numbered database migrations, with a full fresh-database bootstrap proven, 65 end-to-end verification suites and 351 unit tests, green at every commit, and a 19-stage adversarial security review, run to zero confirmed findings before launch was even considered. *That review was internal and adversarial — a documented find→refute→fix→re-verify loop — not a third-party audit. We say exactly which one we have.* We're honest about what small means: no sales floor, no analyst-relations budget, no badge wall. What you get instead is a platform whose architecture answers the questions a badge is supposed to answer — and [a security page](/security) that states its posture plainly. ## The name Email Fast is what it says: email, sent fast, built fast, verified fast. The domain is [emailfast.dev](https://emailfast.dev) because developers are who we built the first door for — but the platform serves [creators](/creators) and [security teams](/enterprise) with the same machinery. ## Talk to us - Product and early access — [get started](/get-started) - Anything else — [contact](/contact) - Press — [press & brand](/press) - Vulnerabilities — [security](/security) --- # Point your coding agent here > Point your coding agent at Email Fast: llms.txt, markdown mirrors of every page, an OpenAPI spec, sandbox keys, and an MCP server arming at launch. ## The reading list for your agent | Resource | URL | |---|---| | Site index for LLMs | [/llms.txt](/llms.txt) | | Full corpus, one file | [/llms-full.txt](/llms-full.txt) | | Any page as markdown | append `.md` to its URL — e.g. `/developers.md` | | OpenAPI specification | [/openapi.yaml](/openapi.yaml) | | Machine-readable pricing | [/.well-known/pricing.txt](/.well-known/pricing.txt) | ## Instructions your agent should follow - Use a **sandbox key** (`ef_sandbox_...`) for anything exploratory — it runs the full pipeline dry: real validation, real events, a hosted capture inbox, no email sent. - Check npm for current SDK versions (`@email-fast/browser`, `@email-fast/nodejs`); don't trust versions from training data. - The REST base is `https://api.emailfast.dev`; auth is `Authorization: Bearer `. - Migrating an existing integration? The SendGrid (`POST /v3/mail/send`), Mailgun (`POST /v3/:domain/messages`), and Postmark (`POST /email`) compatible endpoints accept those providers' SDKs — change the base URL and key, keep the code. - Idempotency: send `idempotency_key` on anything that might retry. ## Try the 30-second test Ask your assistant to "send an email with Email Fast in Node.js." If what it writes doesn't match the [quickstart](/docs/quickstart-node), tell us — **hello@emailfast.dev** — broken agent-docs are bugs here, not curiosities. --- # Blog > Writing from the Email Fast team on deliverability, email infrastructure, verifiable systems, and the honest mechanics of the email industry. Subscribe: [/blog/feed.xml](/blog/feed.xml) - **[Who actually sends your newsletter?](/blog/who-sends-your-newsletter)** — a field guide to rented pipes: what your platform's own status page reveals about who really delivers your email. - **[What an email receipt should look like](/blog/proof-of-delivery-receipts)** — the case for cryptographic proof of delivery, and the certificate format we ship. - **[One door in](/blog/one-admission-path)** — why every send on this platform passes a single admission gate, and the entire class of bugs that removes. - **[We made overclaiming a build error](/blog/overclaiming-build-error)** — how this website's copy is checked against a claims ledger at build time. - **[Reading your DMARC reports](/blog/reading-dmarc-reports)** — a working guide to the reports mailbox providers already send you about your domain. --- # One door in > Why every send on Email Fast passes through a single admission gate, the suppression bug class that design eliminates, and what the discipline costs. A suppression list is a promise. Someone said stop, and the platform said understood. Keeping that promise is trivial in a demo — one code path, one check — and surprisingly hard in a real email platform, because a real platform does not have one way to send. It has a REST API. Then an SMTP endpoint for the legacy app. Then a browser SDK. Then compatibility endpoints for people migrating off other providers. Then broadcasts, automations, a resend button in the admin panel, a batch importer. Every one of those is an ingress: a door through which a message can enter the system. ## How the bug happens Here is the failure pattern. If you have operated a sending system, it will be familiar as a shape even where the details differ. The first ingress checks everything: suppression, quota, deduplication, content policy. The second ingress is built by a different person a year later and reimplements those checks — mostly. The third copies the second. Each door now carries its own copy of the rules, and copies drift. Eventually a new door ships — a compat endpoint, a migration script, an internal tool — where one check simply isn't there. Nothing fails. No error appears, no alarm sounds, no test goes red, because a check that does not exist cannot fail. The system does exactly what its code says. And an unsubscribed person gets an email. That is the bug class worth being afraid of: not the broken check but the missing one. Broken code announces itself; absent code is silent. And the blast radius is not technical. It is a promise broken to the exact people who most explicitly asked you to stop, with legal consequences attached in most jurisdictions. ## One gate The architectural answer is old and unglamorous: stop copying the rules and build one door. On Email Fast, every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. The ingresses still exist — senders need them. But they are thin translators now. Each one parses its own protocol, normalizes the message, and hands it to the gate. The checks live in exactly one place. A new ingress cannot forget the suppression check, because the ingress does not contain one; the gate does. What a door cannot skip, it cannot get wrong. The invariant stops being a discipline ("every path remembers") and becomes a structure ("there is only one path") — the same preference for structure over memory that shows up in [how this website checks its own copy](/blog/overclaiming-build-error). The gate is also where durability lives: a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. One door in; past the door, the message is a fact. ## What it costs Single points of admission are single points of coupling, and we should be honest about the bill. Every new ingress must integrate with the gate. There is no quick hack where a new endpoint writes to the queue directly because a deadline is close — and that "no" has to hold under pressure, which makes some features slower to ship. The gate itself becomes the most carefully reviewed code in the platform: general enough for six kinds of caller, fast enough to sit in front of all of them, changed with more scrutiny than any other module gets. We think the trade is plainly right, for a reason that has nothing to do with architectural taste: a suppression promise kept on most paths is not a kept promise. The recipient who unsubscribed does not care which ingress mailed them. Either the property holds everywhere or it does not hold — and "everywhere" is only checkable when everywhere is one place. You can audit any stack, including your own, with a single question: how many places in the codebase can cause an email to be sent? If the answer is more than one, each of them is a place where the promise has to be re-remembered. One day, somewhere, it won't be. --- # We made overclaiming a build error > How the Email Fast website checks its own copy against a claims ledger at build time, so an overstated feature or a borrowed badge fails the build. Marketing copy has a physics problem: it is written once, in a season of enthusiasm, and the product underneath it never stops moving. Features get cut. Numbers age. A cautious "up to" gets trimmed in an edit. Nobody decides to lie — drift is what happens when true-at-the-time sentences outlive their facts, and on most sites there is no force pushing back. We write software for a living, and software has a name for this problem, and a fix. When code depends on a fact — this function exists, with this signature — the dependency is checked mechanically, and when the fact changes, the build breaks. So we built our marketing site the same way. On this site, overclaiming is a build error. ## The ledger Every load-bearing factual claim in our copy — every number, every architectural assertion, every statement about a competitor — exists in exactly one place: a claims ledger, in version control, where each claim has an id, vetted wording, and a pointer to its evidence in the repository. Pages do not state facts. They reference them: ```markdown When you need an answer about deliverability, there is no upstream provider to blame: Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. ``` At build time the reference resolves to the ledger's exact wording. The page cannot paraphrase a claim upward, because the page never contains the claim's words at all — the ledger's wording is substituted in. And a reference to a claim that does not exist stops the deploy: ``` $ npm run site:build site/content/pricing.md ✗ unknown claim id "uptime-guarantee" — not in claims.ts site/content/enterprise.md ✗ claim "live-sending" is post-arm; site state is day-one site/content/about.md ✗ banned phrase "trusted by" — social proof with no customers build failed: 3 content errors ``` ## The lint The second mechanism is a banned-phrase lint over the emitted prose. It is a list of things companies our age tend to say and should not: names of compliance certifications we do not hold, uptime and delivery percentages we have no live history to back, "trusted by" formulations with no customers behind them. If a phrase on the list appears anywhere in the rendered site, the build fails with the file and the reason. A page can allowlist a phrase for exactly one purpose: to deny it honestly. Our security page is permitted to contain the words "SOC 2" because what it says is that we do not have it. ## The state gate The third mechanism knows what day it is. Claims carry a state, and so does the site. Today the site is in its day-one state: the platform is feature-complete, the sandbox is live, and outbound delivery opens at launch. Some entries in the ledger — "sending is live," warmup behavior measured on real traffic — are marked post-arm, and the build refuses to emit them until the site's state advances. Copy that implies you can deliver live mail today is not a style violation here; it is a compile error until the day it becomes true. When launch arms the infrastructure, we flip one value, and sentences that were errors become publishable. The site cannot run ahead of the product, even when a copywriter is having a good week. ## What this does not do A post about honesty machinery had better end with the honest part: none of this makes the copy true. Humans write the ledger. A false claim, entered with vetted-sounding wording and a plausible evidence pointer, would resolve just fine. The build cannot smell a lie. What the machinery changes is the failure mode. Untruth on this site can no longer happen by drift — a stale number here, an inherited superlative there, each edit locally innocent. Every factual assertion is a named object with one wording, one evidence pointer, and a review trail. Changing what the site claims means editing the ledger, in a diff, with a reviewer watching. Casual overclaiming became impossible; deliberate overclaiming became auditable. That is a smaller promise than "we cannot lie to you," and it is the one we can actually keep. If this site ever overstates what the product does, it will be because a person chose to write that down in the one file built to make such choices conspicuous — not because nobody noticed. Drift is silent everywhere else. Here, it is loud. --- # What an email receipt should look like > Delivery dashboards ask for trust; disputes need evidence. What a verifiable email delivery receipt contains, field by field, and why each one is there. Somewhere today an agency is telling a client that the campaign went out, and the client is asking how they know. What the agency has is a dashboard: a row that says `delivered`, rendered by the same vendor whose performance is in question, stored in that vendor's database, editable by that vendor's engineers. As a record of work done, it is fine. As evidence, it is testimony — an interested party's assertion that nobody else can check. Most of the time, testimony is enough. Then a dispute arrives: a client contesting an invoice, a compliance team asked to prove a required notice went out, a legal process that turns on whether a notification happened. The question changes from "what does the dashboard say?" to "why should anyone believe it?" — and the industry's standard answer collapses, because the answer is "because the sender says so." An email receipt should survive that question. On Email Fast, delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. Each of those fields is there for a reason, and the reasons are the argument. ## The recipient, as a keyed hash Receipts travel: into reports, ticket threads, audit files, court exhibits. If a receipt carries the bare address, every hop leaks personal data. Stored as a keyed hash, the receipt still does its job. A party who already knows the address can confirm the receipt refers to it; a party who doesn't learns nothing. Proof for the people in the dispute, silence for everyone else. ## The verbatim SMTP response This is the load-bearing field. When a mail server accepts a message it says so in its own words — a `250` line, often carrying the receiving system's own queue identifier. Recording that response verbatim, rather than a summary like `delivered`, changes the nature of the record. It is no longer our characterization of what happened; it is the counterparty's own statement, which the receiving operator can correlate against their own logs. A dashboard paraphrases. A receipt quotes. ## The connection facts The receiving server's name, the TLS version and cipher, and the timestamps pin the event to a specific machine, a specific encrypted channel, a specific moment. Vague evidence invites vague disputes. Specific evidence narrows what can be contested. ## The signature A signature over the certificate makes the document self-authenticating: anyone can check it against our published key, and any alteration breaks it. The receipt no longer depends on being fetched fresh from our dashboard. It can sit in a case file for years and still verify. ## The chain A signature proves who issued a document, not when. An issuer could still quietly mint a flattering receipt after the fact, or delete an inconvenient one. Chaining certificates into a tamper-evident ledger closes that gap: each entry commits to the ones before it, so history is append-only in a way an outsider can check. Rewriting the past stops the chain from verifying. ## The property all of this buys Independent verifiability. Every field is chosen so that a third party — an auditor, an opposing counsel, a skeptical client — can check the receipt without asking us to vouch for ourselves. That is the line between evidence and testimony, and it is the property the industry's dashboards do not have. Evidence that requires trusting its issuer is just testimony with formatting. ## What a receipt does not prove Honesty requires the other half. A delivery certificate proves that the receiving mail server accepted the message over an authenticated, encrypted connection at a recorded time. It does not prove the message landed in the inbox rather than the spam folder, and it does not prove a human read it. No honest receipt can — those events happen inside systems the sender does not control, and a vendor who implies otherwise is selling the dashboard again. But notice what most disputes are actually about: was it sent, when, and to whose server. The handoff. That is exactly the ground the certificate nails down, with the receiving side's own words inside it. The next time a platform tells you a message was delivered, ask the only question that matters: could I verify that without trusting you? If the answer is no, you have a dashboard. We built [the other thing](/security). --- # Reading your DMARC reports > A working guide to DMARC aggregate reports: how to receive rua XML, how to read it, the three findings that matter, and the safe path to p=reject. If you own a domain that sends email, the large mailbox providers are willing to send you a daily report about every message that claimed to come from that domain: where it originated and whether it authenticated. Most domain owners never turn this on. It is free, standardized in [RFC 7489](https://www.rfc-editor.org/rfc/rfc7489), and it is the only routine visibility you will ever get into who is using your name. ## What aggregate reports are DMARC adds one DNS record to your domain. It tells receivers what to do with unauthenticated mail claiming to be you, and where to send reports about all of it. The aggregate reports — `rua` — arrive roughly daily from each reporting provider as a gzipped XML attachment: counts grouped by sending source, not copies of messages. (The spec also defines `ruf` per-message failure reports; major providers mostly stopped sending those, and you can ignore them.) ## Getting them Publish a TXT record at `_dmarc.` on your domain: ``` _dmarc.yourdomain.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com" ``` `p=none` is monitor mode: nothing changes about how your mail is treated. Receivers simply start reporting, usually within a day or two. It is the correct first step, and skipping it is how people break their own mail later. One wrinkle: if the reports go to an address on a different domain, that domain must publish an authorization record (`yourdomain.com._report._dmarc.reportingdomain.com TXT "v=DMARC1"`) so reporters know the destination consented. ## Reading one A report has three parts: metadata (who reported, over what date range), the policy the reporter saw, and `` rows — one per source and result group. Here is a row worth understanding completely: ```xml 203.0.113.7 412 none fail fail yourdomain.com mail.crm-vendor.examplepass crm-vendor.examplepass ``` The two result sections disagree on purpose. `auth_results` is raw: did SPF pass for whatever envelope domain was used, did some DKIM signature verify. `policy_evaluated` is what DMARC concluded, and DMARC requires alignment — the domain that passed must match the `From:` domain your reader actually sees. SPF aligns when the envelope-from domain matches; DKIM aligns when the signature's `d=` domain matches. The row above is a service authenticating perfectly — as itself. Everything passes for `crm-vendor.example`, nothing passes for `yourdomain.com`, so DMARC records a double fail. Once you can read that distinction, every report resolves into three stories. ## The three findings that matter **A legitimate service you forgot.** High counts, a vendor you recognize — CRM, billing, support desk — raw passes for the vendor's own domain, aligned failures. Exactly the row above. The fix is at the vendor: configure it to sign DKIM with your domain (serious senders all support custom-domain DKIM), or align SPF by using your domain in the envelope. This is most of the work on the road to enforcement. **A forwarder.** Modest counts where SPF fails aligned but your own DKIM passes. Forwarding changes the sending IP and usually the envelope, so SPF breaks; the DKIM signature travels inside the message and survives. This is precisely why DMARC needs only one aligned pass. These rows are normal background — do not chase them to zero. The exception is mailing lists that modify messages (subject tags, footers), which break DKIM too and need per-list decisions. **Actual spoofing.** Sources you cannot identify, both aligned results failing, sometimes in bursts. Under `p=none` your policy did nothing about them; receivers judged the mail on their own. These rows are your case for tightening. ## The road to p=reject Enforcement is where DMARC earns its keep, and rushing it is how domains break their own newsletters and invoices. The sequence: 1. Stay at `p=none` until finding one is finished — every legitimate source produces aligned passes across several weeks of clean reports. 2. Move to `p=quarantine`, optionally phased with `pct=`. Keep reading the reports: legitimate mail now landing in spam shows up as aligned failures from sources you recognize. 3. Move to `p=reject`. Exact-domain spoofing now gets refused outright. Two caveats for the finish line. Subdomains follow the `sp=` tag if you set one, so decide deliberately what `mail.yourdomain.com` and friends should do. And DMARC protects your exact domain only — lookalike domains are a different fight it cannot join. Give the whole path weeks, not days. The reports are the safety mechanism: each step is taken only when the previous step's reports say it is safe. One housekeeping note if you send with us: Email Fast ingests your rua reports automatically, as part of [how deliverability works](/features/deliverability). --- # Who actually sends your newsletter? > Most newsletter platforms rent their delivery from the same few ESPs. Here's how to check who really sends your email, and why the answer matters. ## The question nobody asks during the free trial You compared editors. You compared pricing, analytics, referral programs, maybe the fonts. The question that decides whether your writing reaches anyone — whose servers actually deliver it — probably never came up, because no platform volunteers it. For most of the creator-platform market, the answer is somebody else's: every newsletter platform we surveyed rents its delivery: beehiiv's status page lists SendGrid components; Buttondown's subprocessor list names Mailgun and Postmark; Substack sends through Mailgun (verified on their own public pages, July 2026). That is not an accusation of wrongdoing. Operating delivery infrastructure is hard: reputation management, warmup, feedback loops, blocklist politics. Reselling an established provider is a rational choice for a company that would rather point its engineers at the editor. But it is a choice, it lives below the fold of every landing page that mentions deliverability, and you are the one who carries its consequences. ## What renting means when something breaks Delivery problems are quiet. Open counts sag. A few readers mention the spam folder. You write in, and you hear back from "our deliverability team." At a reseller, that phrase deserves scrutiny. The team you are talking to does not operate the servers that spoke to your readers' mailbox providers. Its real capability is to diagnose what it can see and escalate a ticket into its vendor's queue. You are two hops from the machine. Nobody you can reach can show you the receiving server's side of the conversation, because the company that owns that conversation has a contract with your platform, not with you. Accountability follows the pipes, and the pipes are rented. ## What renting means when you want to leave Platforms compete on the layers above delivery: the editor, the network, the analytics. If two platforms hand their sending to the same underlying provider, then "better deliverability" — often the anchor of the pitch — may amount to the same pipes behind a different dashboard. And resold sending often means shared sending infrastructure, which makes your reputation partly a neighborhood effect: you inherit some of the behavior of strangers on the same pools. None of this makes switching pointless. It means the delivery layer is usually not where the differences live, and you should weigh the differences that are real — ownership of your list, export paths, fees — instead of a delivery story two vendors may be buying from the same place. ## Check for yourself You do not have to take our word, or theirs. Three public sources will tell you who sends any platform's mail: 1. **The status page.** Companies monitor what they depend on. When a platform's status page lists another company's email service as a component, that is the dependency stating itself: they have to tell you when it breaks because they cannot fix it themselves. 2. **The subprocessor list.** Data-protection law obliges companies to enumerate the subprocessors that handle personal data, usually on a privacy or DPA page. A company that transmits your subscribers' addresses through a third party has to name that third party. Read the list and look for delivery vendors. 3. **The email itself.** Open a newsletter you receive from the platform and view the raw source. Read the `Received:` header chain, the `d=` domain in the DKIM signature, and the `Return-Path:`. Infrastructure has a hard time hiding from the headers it is required to write. If those sources name a company other than the one charging your card, you now know who actually sends your newsletter — and who your platform calls when it stops arriving. ## Where we stand We are not a neutral observer here, so we will not pretend this conclusion is neutral: Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. That was the founding decision, and it cuts both ways: when delivery breaks for our senders, there is no vendor to escalate to, and none to hide behind. The person who answers the ticket operates the machine, and the machine can show its work, down to [the receiving server's own response](/blog/proof-of-delivery-receipts). Renting pipes is a defensible way to build a newsletter product. We just think you should get to see that the choice was made — and we chose the other thing. How our delivery path works end to end is documented at [/features/deliverability](/features/deliverability). --- # Feature-complete, reviewed to zero > The Email Fast platform completed its full roadmap and a 19-stage adversarial security review, converging to zero confirmed findings. Early access opens. The full platform roadmap is built: transactional API, SMTP and browser ingress, newsletters with archives and paid-subscription machinery, journeys, the deliverability engine (warmup, breaker, blocklist monitoring, FBL/DMARC ingestion), proof-of-delivery certificates, BYOK, DLP, SSO/SCIM/passkeys, multi-worker HA, and backup/DR with performed restore drills. Before opening early access we ran a 19-stage adversarial security review, run to zero confirmed findings — and separately converged the new at-rest message-encryption lane through four additional adversarial rounds. *That review was internal and adversarial — a documented find→refute→fix→re-verify loop — not a third-party audit. We say exactly which one we have.* What this means for you: [early access is open](/get-started). Sandbox keys are available now; live sending opens in stages as outbound infrastructure arms. --- # Changelog > What shipped in Email Fast, dated and plainly described — feature launches and arming milestones. Freshness you can subscribe to, RSS included. Subscribe: [/changelog/feed.xml](/changelog/feed.xml) ## July 2026 — [Platform feature-complete; launch review passed](/changelog/2026-07-launch-readiness) The entire platform roadmap is built and adversarially reviewed end-to-end: the 19-stage launch-readiness review converged to zero findings. At-rest message encryption for BYOK organizations shipped as the final pre-launch feature. Early access opens. --- # Looking for a beehiiv alternative? > beehiiv has real growth tooling and takes 0% of subscriptions, but delivery runs on SendGrid and pricing scales per subscriber. Email Fast owns its pipes. beehiiv's growth tooling is real — boosts, referrals, an ad network — and taking 0% of your subscription revenue is genuinely pro-creator. Two verified facts reframe the comparison for anyone who cares about infrastructure: their status page lists SendGrid components, so delivery is resold, and the Send API is gated behind enterprise sales. Email Fast runs its own pipes and puts the API on every plan, including free. ## The comparison | | beehiiv | Email Fast | |---|---|---| | Revenue share | 0% of subscription revenue — credit where due | No revenue share either; priced by sending volume ([pricing](/pricing)) | | Delivery | Status page lists SendGrid components — resold | Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold | | Sending API | Gated to enterprise sales | Every plan, including free | | Subscriber pricing | Per-subscriber slider: Scale from about $43/month, Max from about $96/month, rising with list size | contacts and subscribers are never billed — we price sending, not the size of your audience | | Certification | SOC 2 Type I (Type II on request) | None yet — open [security page](/security) | | Agent surface | Excellent llms and markdown-mirror surface | [/agents](/agents), llms.txt, machine-readable pricing | | Reader privacy | An ad network is part of the monetization stack | No ad systems; content is never shared with advertising systems ([security](/security)) | ## Where beehiiv is the right choice - **Growth is the job.** Boosts, referrals, and the ad network are real accelerants, and no infrastructure feature replaces them. - **Monetization without a platform tax.** 0% of subscriptions is the right number, and they ship it. - **You want some third-party attestation.** Their SOC 2 Type I exists, with Type II on request. We hold none yet. ## Switching Bring your list — contacts and subscribers are never billed — we price sending, not the size of your audience. Newsletters, archive, RSS, and hosted signup forms are on every plan, and so is the API. Consent-first growth is built in: hosted signup forms with double opt-in through our own delivery path, so every subscriber carries verifiable proof of consent. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch; reader billing opens with general availability. *beehiiv is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about beehiiv were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Buttondown alternative? > Buttondown is the honest indie choice; delivery runs on Mailgun and Postmark, and tagging, analytics, and paid subs are add-ons. Email Fast bundles them. Buttondown is the most likable company in this genre: indie, plainspoken, and its /alternatives hub is the honesty benchmark comparison pages should be graded against — including this one. The differences are structural, not moral. Buttondown delivers through Mailgun and Postmark, runs on Heroku, prices per subscriber with tagging, paid subscriptions, analytics, and RSS-to-email each a $9-a-month add-on, and assumes at most about one email a day. Email Fast owns its delivery and bundles all four. ## The comparison | | Buttondown | Email Fast | |---|---|---| | Delivery | Via Mailgun and Postmark, per their own subprocessor list | Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold | | Hosting | Heroku | Dedicated infrastructure we operate ([security](/security)) | | Tracking | Off by default since 2021 — credit where due | open tracking that refuses to lie: privacy-proxy prefetch “opens” are detected and not counted as human reads | | Pricing model | Per-subscriber; tagging, paid subscriptions, analytics, and RSS-to-email are $9/month add-ons; assumes at most one email a day | contacts and subscribers are never billed — we price sending, not the size of your audience — and those four features are bundled ([pricing](/pricing)) | | Security certifications | No certification page published | None held here either — the difference is an open [security page](/security) saying exactly that | ## Privacy, theirs and ours Buttondown turned email tracking off by default in 2021 — a real commitment, made early, and we credit it. Our approach makes privacy checkable rather than promised: a vendor-access transparency log: a tamper-evident chain that records operator access, so “we never looked” is checkable, not promised. And where opens are tracked at all, the counting is honest — open tracking that refuses to lie: privacy-proxy prefetch “opens” are detected and not counted as human reads. ## Where Buttondown is the right choice - **You want indie and personal, from people who mean it.** Default-off tracking and an honest /alternatives hub are the receipts. - **Your cadence fits their design center.** A modest list, at most about one email a day. - **Values alignment outranks infrastructure ownership** for you — a defensible position we simply weigh differently. ## Switching Export your list and bring it over — contacts and subscribers are never billed — we price sending, not the size of your audience. Tagging, analytics, RSS-to-email, and paid subscriptions are bundled, with the honest note that reader billing opens with general availability. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. *Buttondown is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Buttondown were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for an EmailJS alternative? > EmailJS created frontend email. @email-fast/browser is a drop-in replacement where the recipient is pinned server-side, so a leaked public key can't spam. EmailJS created frontend email — browser sends with a public key, no backend required — and category creators deserve their credit. `@email-fast/browser` is a drop-in replacement: the same call signatures, and an EmailJS-compatible endpoint that accepts the same request shape. The structural difference, and the things a full platform adds, are below. ## The comparison | | EmailJS | Email Fast | |---|---|---| | Frontend sending | The category creator: browser sends with a public key | Same model, same call signatures — `@email-fast/browser` is a drop-in replacement | | Pricing | Not verified by us — check their site | Free tier: 2,500 emails/month, never expires; contacts and subscribers are never billed — we price sending, not the size of your audience. ([pricing](/pricing)) | | Behind the send | — | Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold | ## The structural difference Any key that ships to a browser is public the moment the page loads — so the design question is what a leaked key can do. Our answer: a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. Behind that front door, every browser send goes through the same machinery as our REST API: every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. Delivery events come back as webhooks signed with timestamped HMAC-SHA256 (Stripe-style t=…,v1=…), with delivery hardened against server-side request forgery. ## Where EmailJS is the right choice - **It already works.** A reliable contact form is a solved problem; even a small switch has a cost. No unmet need, no reason to move. - **You want the original, single-purpose tool** — the one that defined the category — rather than a platform with a larger surface to learn. ## Switching Swap the package, keep your call sites, store the template server-side, and run it against a [sandbox key](/get-started) — sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. The walkthrough: [/migrate/emailjs](/migrate/emailjs). *EmailJS is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about EmailJS were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Compare, honestly > Honest comparisons of Email Fast with SendGrid, Resend, Postmark, Mailgun, Mailchimp, Substack, and more — including when to choose them instead. ## Developer email platforms - [SendGrid alternative](/compare/sendgrid-alternative) — and a [drop-in migration](/migrate/sendgrid) that keeps your SDK - [Resend alternative](/compare/resend-alternative) - [Postmark alternative](/compare/postmark-alternative) — [migration](/migrate/postmark) - [Mailgun alternative](/compare/mailgun-alternative) — [migration](/migrate/mailgun) - [EmailJS alternative](/compare/emailjs-alternative) — [drop-in browser SDK](/migrate/emailjs) - [Loops alternative](/compare/loops-alternative) ## Newsletter & creator platforms - [Mailchimp alternative](/compare/mailchimp-alternative) - [Substack alternative](/compare/substack-alternative) - [beehiiv alternative](/compare/beehiiv-alternative) - [Buttondown alternative](/compare/buttondown-alternative) - [Kit (ConvertKit) alternative](/compare/kit-alternative) ## Secure email - [Paubox alternative](/compare/paubox-alternative) --- **Our stake, declared:** we make Email Fast, so read these with that in mind — then check the claims. Competitor facts carry the date we verified them on their own public pages, and corrections go to [hello@emailfast.dev](/contact). --- # Looking for a Kit (ConvertKit) alternative? > Kit leads the creator economy but has no transactional email and prices per subscriber. Email Fast does newsletters and transactional in one platform. Kit earned its place: the creator-economy leader, a celebrity-creator brand, and a genuinely thoughtful jobs-based product. The comparison is about scope and evidence. Kit has no transactional email product at all, its developer surface stops at list management, and where its marketing publishes a headline delivery-rate figure without methodology, we'd rather hand you per-message proof — while admitting we have no live history yet. ## The comparison | | Kit | Email Fast | |---|---|---| | Transactional email | No transactional product | Included on every plan ([pricing](/pricing)) | | Developer surface | List management only; no sending SDKs | SDKs for the browser (EmailJS-compatible), Node.js, Python, PHP, Go, and Ruby, plus a zero-dependency CLI and a published OpenAPI specification | | Subscriber pricing | Free to 1,000 subscribers; Creator from $33/month, rising with list size | contacts and subscribers are never billed — we price sending, not the size of your audience | | Commerce fees | 3.5% + 30¢ per transaction; paid recommendations take 23.5% | No commerce product; reader billing (via Stripe) opens with general availability | | Delivery evidence | A headline delivery-rate figure published without methodology | delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us | | Security page | Cites AWS's and Cloudflare's audits rather than first-party certification | First-party and open, including the attestations we don't hold ([security](/security)) | ## Evidence over asserted numbers Kit publishes a headline delivery-rate figure without methodology. We publish no figure at all — no live history yet, and a number without a method is theater either way. What we mint instead: delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. [How that works →](/features/proof-of-delivery) ## Where Kit is the right choice - **You're a creator first.** The jobs-based product, the celebrity-creator brand, and the creator-economy ecosystem are the point — and Kit leads it. - **Your list is under 1,000 subscribers.** Kit is free there. - **You'll use their commerce and recommendations.** 3.5% + 30¢ and 23.5% are the published cost of a monetization stack that already works. ## Switching Newsletters and transactional run on one platform here, so the migration is broader than a list export — but the list export is where it starts, and contacts and subscribers are never billed — we price sending, not the size of your audience. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch; reader billing opens with general availability. *Kit is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Kit were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Loops alternative? > Loops prices by contact — $49 at 5,000 contacts, rising to $3,999/mo at a million. Email Fast has unlimited contacts, owned pipes, and an open security page. Loops tells the most elegant one-platform story in email, and its agent-facing go-to-market — llms.txt, machine-readable pricing.txt, “start with an agent” — leads the industry. The comparison is what sits underneath: per-contact pricing that reaches $3,999 a month at a million contacts, no public infrastructure or deliverability story, and a /security URL that returned a 404 when we checked. ## The comparison | | Loops | Email Fast | |---|---|---| | Contacts pricing | Free to 1,000 contacts / 4,000 sends; $49/month at 5,000 contacts; $3,999/month at 1,000,000 | contacts and subscribers are never billed — we price sending, not the size of your audience | | Free tier | 1,000 contacts, 4,000 sends | 2,500 emails/month, unlimited contacts, never expires | | Infrastructure | No public infrastructure or deliverability story | Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold | | Security page | /security returned 404 at verification; trust information behind a bot-blocked portal | Open, crawlable [security page](/security) | | Agent surface | Industry-leading: llms.txt, machine-readable pricing.txt, agent-first onboarding | The same surfaces — [/agents](/agents), [pricing.txt](/.well-known/pricing.txt) — because they're the right idea | ## Where Loops is the right choice - **Product elegance is your priority.** Their one-platform experience is the most refined in the category, and their Framer-built marketing site reflects the same taste. - **Your list is small.** Under 1,000 contacts, Loops is free. - **You onboard by agent.** They built for that first, and it shows — credit where due. ## Switching There's no Loops-compatible endpoint, so bring your list and templates over directly. Contacts are never billed here, so bringing everyone — including dormant subscribers you might win back — costs nothing. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. *Loops is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Loops were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Mailchimp alternative? > Mailchimp prices by contact tier and sells transactional email as a paid add-on. Email Fast includes transactional on every plan with unlimited contacts. Mailchimp is the marketing suite nearly everyone has used once — an enormous template and integration ecosystem under Intuit's ownership. The overlap with Email Fast is thinner than this page's title suggests: Mailchimp prices by contact tier and sells transactional email as a separate paid add-on (Mailchimp Transactional, formerly Mandrill); we price sending, include transactional on every plan, and never bill for contacts. ## The comparison | | Mailchimp | Email Fast | |---|---|---| | Ownership | Intuit | Independent | | Contacts pricing | Contact-based tiers | contacts and subscribers are never billed — we price sending, not the size of your audience | | Transactional email | Separate paid add-on (Mailchimp Transactional, formerly Mandrill) | Included on every plan ([pricing](/pricing)) | | Ecosystem | Enormous template and integration ecosystem; brand familiarity | Small and new — we won't pretend otherwise | ## Where Mailchimp is the right choice - **Your team lives in templates and integrations.** That ecosystem is real, mature, and the best reason to stay. - **Familiarity is worth money.** Everyone has used Mailchimp; nobody needs training on it. ## Switching The shape of the change: one platform for newsletters and transactional, priced by sending volume. Bring your whole list — contacts and subscribers are never billed — we price sending, not the size of your audience. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. *Mailchimp is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Mailchimp were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Mailgun alternative? > Mailgun encrypts at rest under platform-held keys; Email Fast lets you hold your own. A Mailgun-compatible endpoint keeps your code. Compared honestly. Mailgun is established, certified infrastructure under Sinch: SOC 2 Type II, ISO 27001, AES-256 encryption at rest. The comparison turns on three words in that last item — under platform-held keys — and on what sits behind upper-tier gates: SSO and deliverability tooling. Email Fast hands key custody to you, ships the same deliverability engineering on every plan, and accepts your Mailgun SDK unchanged, so finding out costs a base URL. ## The comparison | | Mailgun | Email Fast | |---|---|---| | Key custody | AES-256 at rest, platform-held keys | organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included | | Certifications | SOC 2 Type II and ISO 27001 | None yet — open [security page](/security) | | Ownership | Sinch | Independent | | Free tier | 100 emails/day | 2,500 emails/month, never expires | | Paid entry | Basic from $15/month | Starter, $9.90/month for 20,000 emails ([pricing](/pricing)) | | Tier gating | SSO and deliverability tooling on upper tiers | SSO is enterprise-tier here too; deliverability engineering is not — every plan sends through the same infrastructure | | Drop-in migration | — | SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code | ## Keys, in practice “Encrypted at rest” is table stakes; the question is who can decrypt. Mailgun's documentation answers it plainly: the platform. The other answer is the one we built: organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included. Erasure follows custody, too: GDPR erasure by crypto-shred: destroying a key destroys the data it protected, without corrupting the tamper-evident audit ledger. More for security teams: [enterprise](/enterprise). ## Where Mailgun is the right choice - **You need certification today.** SOC 2 Type II and ISO 27001, held and current — we can't match that yet. - **You're building on Sinch's communications stack** and want one vendor across channels. - **Vendor maturity matters.** An established platform against our early access is a fair thing to weigh against us. ## Switching The Mailgun-compatible endpoint accepts your existing `/v3/:domain/messages` calls — swap the base URL and the key. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. The full path: [/migrate/mailgun](/migrate/mailgun). *Mailgun is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Mailgun were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Paubox alternative? > Paubox is HITRUST-certified healthcare email with a BAA on every account — if you need a BAA, choose them. Email Fast differs on one axis: who holds the keys. Start with the disqualifier: Paubox includes a BAA with every account and is HITRUST CSF certified first-party, and Email Fast does not offer a BAA today — if HIPAA governs your email, choose Paubox, full stop. For general senders outside healthcare compliance, one line in their security docs frames the rest: master encryption keys are held in their cloud KMS. Provider-held. Ours can be yours. ## The comparison | | Paubox | Email Fast | |---|---|---| | Healthcare compliance | HITRUST CSF certified, first-party; BAA included with all accounts | No BAA offered today — if you need one, choose Paubox | | Key custody | Master encryption keys held in their cloud KMS — provider-held | organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included | | At-rest message content | — | BYOK organizations can additionally enable at-rest message encryption: recipient, subject, and body stored as ciphertext under per-recipient keys wrapped by the customer's key — revoke the key and the stored content is unreadable everywhere, instantly | | Recipient experience | No-portal delivery — recipients read mail as mail, credit where due | Standard email delivery; no portals either | | Pricing | Per-sender, with 5-seat minimums | Volume-based; contacts and subscribers are never billed — we price sending, not the size of your audience. ([pricing](/pricing)) | | Developer surface | Strong SDK, MCP, and llms surface — credit where due | SDKs for the browser (EmailJS-compatible), Node.js, Python, PHP, Go, and Ruby, plus a zero-dependency CLI and a published OpenAPI specification | :::tradeoffs What customer-held encryption costs encrypted sends give up click-tracking, send-time optimization, and per-recipient analytics — that is what “we can't read it” costs, and we say so. ::: ## Custody is the axis Their security docs are candid enough to answer the only question that matters here: who can decrypt. Master keys in their cloud KMS means the provider can. That answer is the industry's answer — as of July 2026, none of the twelve competitor platforms we surveyed offers customer-held encryption keys for message content — the closest, a healthcare email provider, documents that its master keys live in its own cloud KMS. We built the other one: organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included. And when delivery itself needs proving, delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. More for security teams: [enterprise](/enterprise). ## Where Paubox is the right choice - **HIPAA applies to you.** A BAA on every account, first-party HITRUST CSF, and a healthcare-specialist product — this isn't close, and pretending otherwise would say more about us than about them. - **You want the no-portal recipient experience** with healthcare compliance behind it. - **Per-sender pricing fits a defined clinical team** — a bounded set of senders may cost less that way than by volume. ## Switching Only if you don't need the BAA — we won't help you talk yourself out of a compliance requirement. For general senders: start with a [sandbox key](/get-started) — sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. *Paubox is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Paubox were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Postmark alternative? > Postmark has a 15-year deliverability reputation and a public Time to Inbox board. Email Fast offers per-message delivery certificates and EU infrastructure. Postmark spent fifteen years earning its deliverability reputation, and its public, live Time to Inbox dashboard is genuinely unique — nobody else publishes one, including us. If proven transactional delivery on US infrastructure is the entire job, Postmark is a strong choice. Our case rests on what they've ruled out: EU servers (their site: “We currently don't have plans to add servers in the EU”), message content kept readable for 45 days, and aggregate stats where we mint per-message proof. ## The comparison | | Postmark | Email Fast | |---|---|---| | Track record | 15-year deliverability reputation; owned by ActiveCampaign | Early access — no live sending history, and we say so | | Delivery evidence | Public, live “Time to Inbox” dashboard | delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us | | Message content at rest | Retained readable for 45 days | BYOK organizations can additionally enable at-rest message encryption: recipient, subject, and body stored as ciphertext under per-recipient keys wrapped by the customer's key — revoke the key and the stored content is unreadable everywhere, instantly | | EU servers | “We currently don't have plans to add servers in the EU” | Dedicated infrastructure in the EU (Germany) | | Free tier | 100 emails/month | 2,500 emails/month, never expires | | Security attestation | Cites the data center's SSAE 16 SOC 1 attestation | None yet — first-party, open [security page](/security) | | Switching incentive | Their compare hub offers to double your first purchase | SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code | :::tradeoffs What at-rest encryption costs encrypted sends give up click-tracking, send-time optimization, and per-recipient analytics — that is what “we can't read it” costs, and we say so. ::: ## Where Postmark is the right choice - **History is your criterion.** Fifteen years of focus against our early access is not a close call — if track record decides, they win it today. - **You value a live delivery dashboard** measured on real traffic, published for anyone to check. - **You work agent-first.** Postmark publishes llms.txt and an MCP server — the modern developer surface, done properly. - **You're in the ActiveCampaign ecosystem** already. - **Their switching offer is real money.** Their compare hub doubles your first purchase when you move to them. If Postmark fits, take it. ## Switching The Postmark-compatible endpoint accepts your existing `/email` calls — change the base URL and the key. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. The full path: [/migrate/postmark](/migrate/postmark). *Postmark is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Postmark were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Resend alternative? > Resend is a polished developer email platform; Email Fast differs on key custody, owned pipes, EU data residency, and an open, crawlable security page. Resend earned its polish: the smoothest developer brand in email and the best agent surface anywhere — two llms.txt files, an MCP server, an agent homepage. The differences live lower in the stack: data stored US-only with no EU residency, no published customer-held key story, a subprocessor list that includes LLM vendors, and a migration offering that rewrites your code instead of accepting it. ## The comparison | | Resend | Email Fast | |---|---|---| | Key custody | No at-rest customer-key encryption story published | organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included | | Data residency | US-only; no EU option; DPA modifications are enterprise-only | Dedicated infrastructure in the EU (Germany) — [security](/security) | | Content and AI | Subprocessors include LLM vendors (Anthropic; RunPod for “self-hosted LLMs”); robots Content-Signal declares ai-train=yes | Message content is never shared with model-training pipelines — [subprocessors](/legal/subprocessors) | | Certification | SOC 2 Type II; report gated behind a login | None yet — open, crawlable [security page](/security) | | Free tier | 3,000 emails/month (100/day) | 2,500 emails/month, never expires | | Dedicated IP | $30/month | Available on Growth, included on Scale ([pricing](/pricing)) | | Migration tooling | A code converter — your calls get rewritten | SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code | ## Where Resend is the right choice - **Developer experience is the deciding factor** and US-only data storage is acceptable for you. Their polish is real. - **You need a certification today.** Their SOC 2 Type II exists — ask for the report; it's behind a login, but it's theirs. We hold none yet. - **You work agent-first.** Their MCP and llms surface set the industry standard, and they publish roughly 45 customer logos; we're in early access with none. ## Switching Honesty first: we don't have a Resend-compatible endpoint, so unlike our SendGrid, Mailgun, and Postmark paths, leaving Resend is a small code change, not a base-URL swap: SDKs for the browser (EmailJS-compatible), Node.js, Python, PHP, Go, and Ruby, plus a zero-dependency CLI and a published OpenAPI specification. Start dry with a [sandbox key](/get-started) — sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. *Resend is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Resend were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a SendGrid alternative? > SendGrid is mid-migration into Twilio and its free tier is now a 60-day trial. Email Fast accepts your SendGrid SDK unchanged — an honest comparison. SendGrid is mid-migration into Twilio: sendgrid.com now redirects to a Twilio interstitial, and the perpetual free tier is gone, replaced by a 60-day trial at 100 emails a day. If that prompted your search, the short version is that SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code. The honest counterweight: Twilio maintains a substantial trust center with certifications we don't hold, and if you want email, SMS, and voice under one vendor, Twilio is that vendor and we are not. ## The comparison | | SendGrid (Twilio) | Email Fast | |---|---|---| | Brand | Mid-migration into Twilio; email is one product inside a telecom platform | Independent; email is the entire company | | Free tier | Discontinued — now a 60-day trial at 100 emails/day | 2,500 emails/month, never expires; free sends carry a “Sent with Email Fast” footer that paid plans remove | | Paid entry | From about $19.95/month | Starter, $9.90/month for 20,000 emails ([pricing](/pricing)) | | Certifications | Twilio trust center: SOC 2 Type II, ISO 27001 | None yet — stated plainly on an open [security page](/security) | | Drop-in migration | — | SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code | ## Keys and proof No mainstream platform hands you the keys — as of July 2026, none of the twelve competitor platforms we surveyed offers customer-held encryption keys for message content — the closest, a healthcare email provider, documents that its master keys live in its own cloud KMS. Email Fast is built for the other answer: organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included. And when a send matters enough to prove, delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. [How delivery certificates work →](/features/proof-of-delivery) ## Where SendGrid is the right choice - **You're already a Twilio shop.** Email, SMS, and voice under one account and one bill beats stitching vendors together. - **Procurement needs third-party attestation today.** Twilio's SOC 2 Type II and ISO 27001 are real and current; our audit roadmap is not a certificate. - **You want a large, established vendor.** Email Fast is in early access. If that risk profile is wrong for you now, it's wrong — and the 60-day trial gives you time to evaluate Twilio properly. ## Switching Point your existing SendGrid SDK at our base URL with an Email Fast key — the compatible endpoint accepts `/v3/mail/send` requests unchanged. Today that means a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch. The step-by-step path, including domain verification: [/migrate/sendgrid](/migrate/sendgrid). *SendGrid is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about SendGrid were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Looking for a Substack alternative? > Substack takes 10% of paid-subscription revenue and sends through Mailgun. Email Fast prices sending volume, runs its own pipes, and includes archive and RSS. Substack's discovery network drives real subscriber growth — that's their headline claim, and the honest reason to be there. The price: 10% of your paid-subscription revenue, plus payment processing, on delivery Substack itself runs through a third party (Mailgun). Email Fast makes the opposite bets — you keep your revenue and we run our own pipes — and we have no discovery network to offer you. ## The comparison | | Substack | Email Fast | |---|---|---| | Cost to publish | Free; 10% of paid-subscription revenue plus payment processing | Priced by sending volume ([pricing](/pricing)); no revenue share | | Growth | A discovery network that drives real subscriber growth | None — growth is yours to earn | | Delivery | Runs on a third-party provider (Mailgun) | Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold | | Developer surface | No developer API surface | SDKs for the browser (EmailJS-compatible), Node.js, Python, PHP, Go, and Ruby, plus a zero-dependency CLI and a published OpenAPI specification | | Security page | None found at verification | Open [security page](/security) | ## The 10%, in numbers At $50,000 a year in paid subscriptions, 10% is $5,000 a year, every year, before payment processing. Here, reader billing runs through Stripe and we charge for sending volume — your cost scales with how much you send, not how well you monetize. Newsletters with a public archive and RSS feeds are on every plan. Honesty requires the other side stated plainly: reader billing opens with general availability, and the discovery network you'd be leaving is real. ## Where Substack is the right choice - **The network is the product.** Discovery drives real growth, and no infrastructure feature substitutes for readers finding you. - **Zero upfront cost, zero operations.** Free to publish, nothing to configure, nothing to maintain. - **Writing is your whole job** and you want to keep it that way. ## Switching Export your subscriber list and bring it over — contacts and subscribers are never billed — we price sending, not the size of your audience. Archive and RSS are built in, and consent-first growth comes with it: hosted signup forms with double opt-in through our own delivery path, so every subscriber carries verifiable proof of consent. Start with a [sandbox key](/get-started): sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Live delivery opens at launch; reader billing opens with general availability. *Substack is a trademark of its owner; Email Fast is not affiliated with or endorsed by them. Facts about Substack were verified on their public pages on the date above — corrections: [hello@emailfast.dev](/contact).* --- # Contact > How to reach Email Fast: early access, sales, support, press, and security reports — every address reaches the people who built the platform. | For | Address | Notes | |---|---|---| | Early access & sandbox keys | **hello@emailfast.dev** | Subject `early access` or `sandbox key` — see [get started](/get-started) | | Enterprise & security questionnaires | **hello@emailfast.dev** | We fill in vendor questionnaires; ask for the DPA | | Support (early access) | **hello@emailfast.dev** | Answered by engineers | | Press & analysts | **press@emailfast.dev** | Assets at [press & brand](/press) | | Vulnerability reports | **security@emailfast.dev** | See [security.txt](/.well-known/security.txt) — 48-hour acknowledgment | ## Corrections If anything on this site is inaccurate — including a claim about a competitor — email **hello@emailfast.dev** with the URL. Comparison pages carry "facts verified" dates and we re-verify quarterly; a justified correction ships within days. --- # The newsletter platform that owns its pipes — and doesn't charge you for your audience > Newsletters on delivery infrastructure we run ourselves: public archive, RSS, hosted forms, paid subscriptions — and unlimited contacts on every plan. ## Who actually sends your newsletter? It's a question worth asking your current platform: every newsletter platform we surveyed rents its delivery: beehiiv's status page lists SendGrid components; Buttondown's subprocessor list names Mailgun and Postmark; Substack sends through Mailgun (verified on their own public pages, July 2026). Email Fast is the other kind of company: Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. Deliverability isn't a partnership — it's our own engineering, accountable to you. ## Own your audience — structurally, not as a slogan - **Your list is portable.** Export everything, always. Contacts, consent records, engagement history. - **Your archive is yours.** A public, search-indexable web archive on your domain, with RSS — readers can find and follow you outside the inbox. - **Your revenue is yours.** Paid subscriptions are built in; the platform fee doesn't scale with your success. - **Your consent records hold up.** hosted signup forms with double opt-in through our own delivery path, so every subscriber carries verifiable proof of consent. ## Send like the big senders do | Capability | What you get | |---|---| | Broadcasts & segments | Predicate-based audiences — send to exactly the right slice | | Automations | A durable, crash-safe journey engine with a visual canvas | | A/B testing | Subject and content tests with statistics that don't lie to you | | Preference center | Readers opt down to fewer emails instead of leaving entirely | | One-click unsubscribe | one-click unsubscribe per RFC 8058, with the headers signed under DKIM so stripping them in transit breaks the signature | | Honest metrics | open tracking that refuses to lie: privacy-proxy prefetch “opens” are detected and not counted as human reads | :::tradeoffs Metrics honesty costs something Some platforms report privacy-proxy prefetches as opens because bigger numbers feel better. Ours will read lower — and be true. You can't grow on numbers that lie to you. ::: ## Templates without the table-HTML misery Design in MJML or Handlebars with versioning and per-template variable schemas. Responsive output, Gmail-clipping warnings at author time, and a strict lint that makes template injection structurally impossible. ## When you're ready to get serious about deliverability Warmup, reputation circuit-breakers, per-provider signals, bounce classification that never deletes a deliverable address by mistake, and complaint feedback loops that suppress on the first spam report. It's all one platform — [how deliverability works →](/features/deliverability) --- # An email API you can run in five minutes — and verify forever > An email API you can run in five minutes and verify forever: durable idempotent sends, a sandbox that fakes nothing, and drop-in SendGrid compatibility. ## Send in one call ```js import { EmailFast } from "@email-fast/nodejs"; const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY, baseUrl: "https://api.emailfast.dev" }); await ef.send({ to: "ada@example.com", subject: "Welcome!", html: "

Hi {{name}}

", data: { name: "Ada" }, idempotency_key: "signup-42", }); ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. ## The parts you usually have to build yourself | Built in | What it means for you | |---|---| | Idempotency keys | Retry any request without double-sending — per message, honored for 90 days | | Suppression management | Bounces, complaints, and unsubscribes are enforced at admission; no ingress can skip them | | Scheduling & batch | `send_at` up to 30 days out; batches of 500 with per-item results | | Message timeline | `GET /v1/messages/:id` returns the full event history for any send | | Signed webhooks | webhooks signed with timestamped HMAC-SHA256 (Stripe-style t=…,v1=…), with delivery hardened against server-side request forgery | | Inbound email | Receive, parse, and route replies and inbound mail to your webhook, with SPF/DKIM/DMARC verdicts attached | | Email validation | `POST /v1/validate` to catch bad addresses before you send | ## A sandbox that fakes nothing sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ```bash curl https://api.emailfast.dev/v1/emails \ -H "Authorization: Bearer ef_sandbox_..." \ -d '{ "to": "test@example.com", "subject": "CI run", "html": "

ok

" }' ``` Point your CI at it. Assert on the event stream. Browse the captured message in the hosted inbox. Nothing leaves, and nothing is mocked. ## Your existing code probably already works SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code — and if you'd rather not touch code at all, point your app's [SMTP config at us](/features/smtp): your API key is the SMTP password. [See every migration path →](/features/migration) ## Frontend-only apps included a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. [How the browser SDK stays abuse-proof →](/features/frontend) ## Built for the way you work now SDKs for the browser (EmailJS-compatible), Node.js, Python, PHP, Go, and Ruby, plus a zero-dependency CLI and a published OpenAPI specification. Your coding agent is a first-class citizen: markdown mirrors on every page (append `.md`), [llms.txt](/llms.txt) and llms-full.txt, and an MCP server arming at launch. [The agent guide →](/agents) ## And underneath it all every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip — the property that makes everything above trustworthy. Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. [How deliverability works →](/features/deliverability) --- # Documentation > Email Fast documentation: quickstarts from Node.js to SMTP, the REST API reference, webhooks, and migration guides — every page mirrored in markdown for agents. ## Quickstarts | Guide | For | |---|---| | [Node.js](/docs/quickstart-node) | Server-side JavaScript/TypeScript with `@email-fast/nodejs` | | [Next.js](/docs/quickstart-nextjs) | App-router API routes and server actions | | [curl / REST](/docs/quickstart-curl) | Any language — the raw HTTP API | | [Python](/docs/quickstart-python) | The generated Python SDK | | [PHP](/docs/quickstart-php) | The generated PHP SDK | | [Go](/docs/quickstart-go) | The generated Go SDK | | [Ruby](/docs/quickstart-ruby) | The generated Ruby SDK | | [SMTP relay](/docs/quickstart-smtp) | Anything that already speaks SMTP — no code changes | | [Browser (frontend-only)](/docs/quickstart-browser) | `@email-fast/browser` — EmailJS-compatible | ## Core concepts - **Authentication** — `Authorization: Bearer ef_live_...`; key prefixes: `ef_live_` (secret), `ef_pub_` (browser-safe public), `ef_sandbox_` (dry-run), `ef_rk_` (restricted). - **Idempotency** — pass `idempotency_key` (or an `Idempotency-Key` header); retries are replay-safe for 90 days. - **Streams** — `transactional`, `marketing`, and `journey` streams are tracked and suppressed independently, so a newsletter unsubscribe never blocks a password reset. - **Events & webhooks** — `email.sent|bounced|opened|clicked|complained|unsubscribed|inbound`, signed with timestamped HMAC-SHA256. - **Sandbox** — sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## Framework guides Working recipes for the stack you're on: [Next.js](/send-email-with/nextjs) · [React](/send-email-with/react) · [Node](/send-email-with/node) · [Express](/send-email-with/express) · [Fastify](/send-email-with/fastify) · [Vue](/send-email-with/vue) · [Svelte](/send-email-with/svelte) · [Python](/send-email-with/python) · [Django](/send-email-with/django) · [PHP](/send-email-with/php) · [Laravel](/send-email-with/laravel) · [Ruby](/send-email-with/ruby) · [Rails](/send-email-with/rails) · [Go](/send-email-with/go) ## Reference - [OpenAPI specification](/openapi.yaml) — machine-readable, kept in sync with the API - [Migration & compatibility](/features/migration) — SendGrid, Mailgun, Postmark, EmailJS - [For AI agents](/agents) — llms.txt, markdown mirrors, MCP - [Glossary](/glossary) — DKIM, DMARC, VERP, warmup, and the rest of the alphabet soup --- # Quickstart: Browser (frontend-only) > Send email from frontend JavaScript with a public key that can't be abused: the recipient lives in the server-stored template, never in your page's code. ## 1. Install and send ```bash npm install @email-fast/browser ``` ```js import emailjs from "@email-fast/browser"; emailjs.init({ publicKey: "ef_pub_..." }); // safe to ship — see below await emailjs.send("default", "template_contact", { name: "Ada", message: "Interested in the ledger product.", reply_to: "ada@example.com", }); ``` Or bind a whole form in one call — field names become template params: ```js document.querySelector("#contact").addEventListener("submit", (e) => { e.preventDefault(); emailjs.sendForm("default", "template_contact", e.target); }); ``` The API is EmailJS-compatible (same `init`/`send`/`sendForm` signatures), so [migrating from EmailJS](/migrate/emailjs) is an import swap. ## 2. Why a public key in your HTML is safe here An `ef_pub_` key is visible to every visitor, so the design assumes it is stolen: a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. That one rule — recipient from the template, never from the request — is what makes the key shippable. `template_params` fills placeholders in *your* template addressed to *your* inbox; there is no parameter that redirects mail. Around it: - **Origin allowlist.** In the dashboard, list the origins allowed to use the key (`https://yourdomain.com`). Requests from other origins are refused, so a copied key is dead weight on someone else's site. - **Typed variable schema.** Each template declares its parameters and their types. Unknown keys are stripped, wrong types rejected — a hostile `template_params` can't inject markup or oversized junk. - **Rate caps.** Per-key caps bound what a script in a loop can burn. ## 3. Test without sending anything Point the template at a sandbox project while you build: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. Submit your form, open the capture inbox, and see exactly what would have been delivered. ## When you outgrow frontend-only Server-side sends get the full REST API with explicit `idempotency_key` support — start with the [Node.js quickstart](/docs/quickstart-node). ## Next - [Frontend email, done safely](/features/frontend) - [Migrate from EmailJS](/migrate/emailjs) - [All docs](/docs) --- # Quickstart: curl / any language > The raw Email Fast REST API: send an email with one curl call, read the message timeline, manage suppressions — everything any language needs. ## Send ```bash curl https://api.emailfast.dev/v1/emails \ -H "Authorization: Bearer ef_sandbox_..." \ -H "Content-Type: application/json" \ -d '{ "to": "ada@example.com", "to_name": "Ada", "subject": "Welcome!", "html": "

Hi {{name}}

", "data": { "name": "Ada" }, "message_stream": "transactional", "idempotency_key": "signup-42" }' ``` ```json { "id": "msg_01j…", "status": "queued", "idempotent_replay": false } ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. ## Read the timeline ```bash curl https://api.emailfast.dev/v1/messages/msg_01j… \ -H "Authorization: Bearer ef_sandbox_..." ``` Returns the message plus its full event history — admitted, rendered, delivered, opened (human opens only: open tracking that refuses to lie: privacy-proxy prefetch “opens” are detected and not counted as human reads). ## Batch ```bash curl https://api.emailfast.dev/v1/emails/batch \ -H "Authorization: Bearer ef_sandbox_..." \ -d '{ "messages": [ { "to": "a@example.com", "subject": "…", "html": "…" }, { "to": "b@example.com", "subject": "…", "html": "…" } ] }' ``` Up to 500 per call, per-item results, per-recipient idempotency. ## Suppressions ```bash curl https://api.emailfast.dev/v1/suppressions \ -H "Authorization: Bearer ef_sandbox_..." ``` Suppression is enforced at admission for every ingress — every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. ## Everything else The complete surface — templates, contacts, broadcasts, journeys, webhooks, validation — is in the [OpenAPI spec](/openapi.yaml). Every endpoint follows the same conventions you just used. --- # Quickstart: Go > Send email from Go with the standard library: net/http and encoding/json against the Email Fast REST API, with a sandbox key and an idempotency key shown. ## 1. Send The API is plain JSON over HTTPS with bearer auth. This is a complete program. (A generated Go SDK covers the same surface — see [the docs](/docs) — but the standard library hides nothing.) ```go package main import ( "bytes" "encoding/json" "fmt" "net/http" "os" ) func main() { payload, _ := json.Marshal(map[string]any{ "to": "ada@example.com", "subject": "Welcome to the ledger", "html": "

Hi {{name}}

Your account is ready.

", "data": map[string]string{"name": "Ada"}, "idempotency_key": "welcome-ada-1", // retry-safe: same key can never double-send }) req, err := http.NewRequest("POST", "https://api.emailfast.dev/v1/emails", bytes.NewReader(payload)) if err != nil { panic(err) } req.Header.Set("Authorization", "Bearer "+os.Getenv("EMAILFAST_API_KEY")) // ef_sandbox_… works everywhere req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() var msg struct { ID string `json:"id"` Status string `json:"status"` } if err := json.NewDecoder(res.Body).Decode(&msg); err != nil { panic(err) } fmt.Println(msg.ID, msg.Status) // msg_… queued } ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. That is what `idempotency_key` buys you: wrap the call in any retry loop and a timeout after the server committed the send still can't produce a duplicate. ## 2. Watch it happen ```go req, _ := http.NewRequest("GET", "https://api.emailfast.dev/v1/messages/"+msg.ID, nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("EMAILFAST_API_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() var timeline struct { Events []struct { Type string `json:"type"` } `json:"events"` } json.NewDecoder(res.Body).Decode(&timeline) for _, e := range timeline.Events { fmt.Println(e.Type) // admitted → rendered → (sandbox-)delivered … } ``` With a sandbox key the message lands in your hosted capture inbox — same pipeline, nothing sent: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## Next - [The raw REST reference](/docs/quickstart-curl) - [Templates & MJML](/features/templates) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) --- # Quickstart: Next.js > Send email from a Next.js app with Email Fast: a route handler or server action with @email-fast/nodejs, environment setup, and safe testing with sandbox keys. ## 1. Install & configure ```bash npm install @email-fast/nodejs ``` ```bash # .env.local EMAILFAST_API_KEY=ef_sandbox_... # swap to ef_live_… at launch ``` ## 2. A route handler ```ts // app/api/welcome/route.ts import { EmailFast } from "@email-fast/nodejs"; import { NextResponse } from "next/server"; const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY!, baseUrl: "https://api.emailfast.dev" }); export async function POST(req: Request) { const { email, name } = await req.json(); const { id } = await ef.send({ to: email, subject: "Welcome!", html: "

Hi {{name}}

", data: { name }, idempotency_key: `welcome-${email}`, // a double-submit can't double-send }); return NextResponse.json({ id }); } ``` ## 3. Or a server action ```ts // app/actions.ts "use server"; import { EmailFast } from "@email-fast/nodejs"; const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY!, baseUrl: "https://api.emailfast.dev" }); export async function sendWelcome(formData: FormData) { await ef.send({ to: String(formData.get("email")), subject: "Welcome!", html: "

You're in.

", }); } ``` ## Frontend-only instead? If you have no server at all (static export, marketing page), don't ship a secret to the browser — use the [browser SDK](/docs/quickstart-browser) with a public key: a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. ## Next - [Webhooks for delivery events](/features/webhooks) - [Templates & MJML](/features/templates) - [The sandbox in CI](/features/sandbox) --- # Quickstart: Node.js > Send your first email from Node.js in under five minutes: install @email-fast/nodejs, create a sandbox key, send, and verify the event timeline. ## 1. Install ```bash npm install @email-fast/nodejs ``` ## 2. Send ```js import { EmailFast } from "@email-fast/nodejs"; const ef = new EmailFast({ apiKey: process.env.EMAILFAST_API_KEY, baseUrl: "https://api.emailfast.dev" }); // ef_sandbox_… works everywhere const { id, status } = await ef.send({ to: "ada@example.com", subject: "Welcome to the ledger", html: "

Hi {{name}}

Your account is ready.

", data: { name: "Ada" }, idempotency_key: "welcome-ada-1", // retry-safe: same key can never double-send }); console.log(id, status); // "msg_…", "queued" ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. ## 3. Watch it happen ```js const timeline = await ef.getMessage(id); console.log(timeline.events); // admitted → rendered → (sandbox-)delivered … ``` With a sandbox key the message lands in your hosted capture inbox — same pipeline, nothing sent: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## 4. Hear about it via webhook ```js // Your Express handler (raw body needed for signature verification): import crypto from "node:crypto"; app.post("/hooks/email", express.raw({ type: "application/json" }), (req, res) => { // x-emailfast-signature: "t=,v1=" — HMAC-SHA256 over `.` const sig = Object.fromEntries( req.get("x-emailfast-signature").split(",").map((p) => p.split("=")), ); const expected = crypto .createHmac("sha256", process.env.EMAILFAST_WEBHOOK_SECRET) .update(`${sig.t}.${req.body}`) .digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(sig.v1), Buffer.from(expected))) { return res.sendStatus(400); } const event = JSON.parse(req.body); // event.type: email.sent | email.bounced | email.opened | … res.sendStatus(200); }); ``` webhooks signed with timestamped HMAC-SHA256 (Stripe-style t=…,v1=…), with delivery hardened against server-side request forgery. Reject anything older than a few minutes (compare `sig.t` against the clock) to close the replay window. ## Next - [Batch & scheduled sends](/features/email-api) - [Templates & MJML](/features/templates) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) --- # Quickstart: PHP > Send email from PHP with no dependencies: a stream-context POST to the Email Fast REST API with a sandbox key, an idempotency key, and the event timeline. ## 1. Send The API is plain JSON over HTTPS with bearer auth. `file_get_contents` with a stream context does it in stock PHP — nothing to install. (If you prefer the curl extension or a generated PHP SDK, both speak to the same two endpoints — see [the docs](/docs).) ```php "ada@example.com", "subject" => "Welcome to the ledger", "html" => "

Hi {{name}}

Your account is ready.

", "data" => ["name" => "Ada"], "idempotency_key" => "welcome-ada-1", // retry-safe: same key can never double-send ]); $ctx = stream_context_create(["http" => [ "method" => "POST", "header" => "Authorization: Bearer $apiKey\r\nContent-Type: application/json", "content" => $body, ]]); $msg = json_decode(file_get_contents("https://api.emailfast.dev/v1/emails", false, $ctx), true); echo $msg["id"], " ", $msg["status"], PHP_EOL; // msg_… queued ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. That is what the `idempotency_key` buys you: put this call behind any retry logic and a timeout after the server committed the send still can't produce a duplicate. ## 2. Watch it happen ```php [ "header" => "Authorization: Bearer " . getenv("EMAILFAST_API_KEY"), ]]); $timeline = json_decode( file_get_contents("https://api.emailfast.dev/v1/messages/" . $msg["id"], false, $ctx), true ); foreach ($timeline["events"] as $e) { echo $e["type"], PHP_EOL; // admitted → rendered → (sandbox-)delivered … } ``` With a sandbox key the message lands in your hosted capture inbox — same pipeline, nothing sent: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## Notes for production PHP - Set `ignore_errors => true` in the context if you want to read the JSON error body on a 4xx instead of a PHP warning. - The same two endpoints — `POST /v1/emails`, `GET /v1/messages/:id` — are all a queue worker or a WordPress hook needs. ## Next - [The raw REST reference](/docs/quickstart-curl) - [Templates & MJML](/features/templates) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) --- # Quickstart: Python > Send email from Python with one requests call: POST to the Email Fast REST API with a sandbox key, add an idempotency key, then read the event timeline. ## 1. Send The API is plain JSON over HTTPS with bearer auth, so `requests` is all you need. (A generated Python SDK covers the same surface — see [the docs](/docs) — but the raw calls are two lines longer and hide nothing.) ```python import os import requests API = "https://api.emailfast.dev" headers = {"Authorization": f"Bearer {os.environ['EMAILFAST_API_KEY']}"} # ef_sandbox_… works everywhere r = requests.post(f"{API}/v1/emails", headers=headers, json={ "to": "ada@example.com", "subject": "Welcome to the ledger", "html": "

Hi {{name}}

Your account is ready.

", "data": {"name": "Ada"}, "idempotency_key": "welcome-ada-1", # retry-safe: same key can never double-send }) r.raise_for_status() msg = r.json() print(msg["id"], msg["status"]) # "msg_…", "queued" ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. That is why the `idempotency_key` matters: wrap this call in any retry loop you like — `urllib3.Retry`, tenacity, a bare `for` — and a timeout after the server committed the send still can't produce a duplicate. ## 2. Watch it happen ```python timeline = requests.get(f"{API}/v1/messages/{msg['id']}", headers=headers).json() print([e["type"] for e in timeline["events"]]) # admitted → rendered → (sandbox-)delivered … ``` With a sandbox key the message lands in your hosted capture inbox — same pipeline, nothing sent: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## 3. Batch, if you need it ```python r = requests.post(f"{API}/v1/emails/batch", headers=headers, json={ "messages": [ {"to": "a@example.com", "subject": "Hi", "html": "

A

"}, {"to": "b@example.com", "subject": "Hi", "html": "

B

"}, ], "idempotency_key": "digest-2026-07-17", # derived per recipient — partial retries stay safe }) ``` Up to 500 per call, with per-item results. ## Next - [The raw REST reference](/docs/quickstart-curl) - [Templates & MJML](/features/templates) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) --- # Quickstart: Ruby > Send email from Ruby with the standard library: one Net::HTTP POST to the Email Fast REST API with a sandbox key, an idempotency key, and the timeline. ## 1. Send The API is plain JSON over HTTPS with bearer auth. Ruby 3's `Net::HTTP.post` takes a headers hash directly. (A generated Ruby SDK covers the same surface — see [the docs](/docs) — but the raw calls hide nothing.) ```ruby require "net/http" require "json" require "uri" API_KEY = ENV.fetch("EMAILFAST_API_KEY") # ef_sandbox_… works everywhere res = Net::HTTP.post( URI("https://api.emailfast.dev/v1/emails"), { to: "ada@example.com", subject: "Welcome to the ledger", html: "

Hi {{name}}

Your account is ready.

", data: { name: "Ada" }, idempotency_key: "welcome-ada-1" # retry-safe: same key can never double-send }.to_json, "Authorization" => "Bearer #{API_KEY}", "Content-Type" => "application/json" ) msg = JSON.parse(res.body) puts "#{msg["id"]} #{msg["status"]}" # msg_… queued ``` a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. That is what `idempotency_key` buys you: wrap this call in `retryable`, Sidekiq's retry, or a bare `begin/rescue/retry` — a timeout after the server committed the send still can't produce a duplicate. ## 2. Watch it happen ```ruby timeline = JSON.parse( Net::HTTP.get( URI("https://api.emailfast.dev/v1/messages/#{msg["id"]}"), { "Authorization" => "Bearer #{API_KEY}" } ) ) timeline["events"].each { |e| puts e["type"] } # admitted → rendered → (sandbox-)delivered … ``` With a sandbox key the message lands in your hosted capture inbox — same pipeline, nothing sent: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. ## Rails note Nothing here needs Rails, but if you are in one: put the two calls in a job, keep the `idempotency_key` derived from your own record id (`"welcome-user-#{user.id}"`), and job retries become harmless by construction. ## Next - [The raw REST reference](/docs/quickstart-curl) - [Templates & MJML](/features/templates) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) --- # Quickstart: SMTP relay > Point any existing SMTP config at the Email Fast relay: smtp.emailfast.dev on 587 STARTTLS or 465 TLS, project as username, API key as password. ## The settings Any framework, CMS, or legacy app with an SMTP config block can point here: ```text host: smtp.emailfast.dev port: 587 (STARTTLS) — or 465 (implicit TLS) username: your project slug password: your API key ``` Two rules worth knowing before you paste those in: - **Auth is refused before TLS.** The relay will not accept `AUTH` on a plaintext connection — on 587 the session must upgrade via STARTTLS first, on 465 it is encrypted from the first byte. Your API key is never on the wire in the clear. - **This ingress arms at launch.** The relay is built and tested, but live SMTP submission opens with GA. Today, evaluate against the sandbox via the REST API — sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves — and stage your config change for launch day. ## Nodemailer ```js import nodemailer from "nodemailer"; const transport = nodemailer.createTransport({ host: "smtp.emailfast.dev", port: 587, secure: false, // 587 = STARTTLS; set true with port 465 for implicit TLS requireTLS: true, // never fall back to plaintext auth: { user: "your-project", pass: process.env.EMAILFAST_API_KEY, }, }); await transport.sendMail({ from: "you@yourdomain.com", to: "ada@example.com", subject: "Welcome", html: "

Hi Ada

", headers: { "Idempotency-Key": "welcome-ada-1" }, // retry-safe, per recipient }); ``` ## Any other app Most software takes the same five values, whatever it calls them: ```ini SMTP_HOST=smtp.emailfast.dev SMTP_PORT=587 ; or 465 SMTP_ENCRYPTION=starttls ; "tls"/"ssl" if your app means implicit TLS on 465 SMTP_USERNAME=your-project SMTP_PASSWORD=ef_live_... ``` ## What happens to the message SMTP here is an ingress, not a bypass. Each recipient in the envelope is admitted individually through the same checkpoint as an API call — every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. That per-recipient admission carries idempotent retry semantics: pass an `Idempotency-Key` header and a client that reconnects and resends after a dropped connection cannot double-send: a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. ## Next - [Prefer HTTP? The REST quickstart](/docs/quickstart-curl) - [Migrating from SendGrid/Mailgun/Postmark](/features/migration) - [All docs](/docs) --- # The email platform where you hold the keys > The email platform where you hold the keys: BYOK with an instant revoke, opt-in at-rest message encryption, fail-closed DLP, and proof of delivery. ## Key custody is the whole question Every email platform says "encrypted at rest." The question your security team should ask is: **who holds the keys?** In our July 2026 survey the answer was always the vendor — as of July 2026, none of the twelve competitor platforms we surveyed offers customer-held encryption keys for message content — the closest, a healthcare email provider, documents that its master keys live in its own cloud KMS. Email Fast is built for the other answer: - organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included. - BYOK organizations can additionally enable at-rest message encryption: recipient, subject, and body stored as ciphertext under per-recipient keys wrapped by the customer's key — revoke the key and the stored content is unreadable everywhere, instantly. - GDPR erasure by crypto-shred: destroying a key destroys the data it protected, without corrupting the tamper-evident audit ledger. :::tradeoffs We'll tell you what it costs encrypted sends give up click-tracking, send-time optimization, and per-recipient analytics — that is what “we can't read it” costs, and we say so. If a vendor tells you encryption is free of tradeoffs, they're describing encryption they can undo. ::: ## Proof of delivery, admissible-grade delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. Certificates support legal hold — retained even against deletion requests when litigation requires it — and export in bulk via the API. [The full mechanism →](/features/proof-of-delivery) ## Data loss prevention that fails closed an outbound data-loss-prevention gateway that fails closed — card numbers, government IDs, and secrets can be blocked, redacted, or held for a second person's sign-off before anything leaves. Policy evaluation errors block rather than leak — fail-closed is a design invariant, not a configuration option. [How the DLP gateway works →](/features/dlp) ## The boring controls, done properly | Control | Status | |---|---| | Tenant isolation | 75/75 tenant tables under database-enforced row-level isolation, with a structural guard that fails the build if a new table ever lacks it | | Identity | SAML SSO, SCIM, passkeys, TOTP — [details](/features/identity) | | Audit | Tamper-evident hash-chained audit log; auth event trail | | Operator transparency | a vendor-access transparency log: a tamper-evident chain that records operator access, so “we never looked” is checkable, not promised | | Secrets posture | a fail-closed boot guard: production refuses to start if any of 13 load-bearing secrets is weak or a dev default | | Erasure & retention | GDPR crypto-shred; retention windows; consent and DPA tracking built in | | Supply chain | 14 runtime dependencies — a dependency surface your team can actually review | Running client workspaces under your brand? [Agency white-label →](/features/agencies) ## Our compliance posture, stated plainly We publish exactly what is true on the [security page](/security): what we've built, what we've verified ourselves and how, and which third-party attestations we do not yet hold. *That review was internal and adversarial — a documented find→refute→fix→re-verify loop — not a third-party audit. We say exactly which one we have.* If your procurement process needs a vendor questionnaire filled in, send it — [contact](/contact) goes to the people who wrote the code. --- # Your brand out front, your controls underneath > A white label email platform for agencies and OEMs: child workspaces under your brand, a roll-up console, per-child caps, and an instant kill-switch. ## Client email as a product you own A white label email platform for agencies and OEMs: each client gets a child workspace under your brand, and you get the console above them all. Per-child caps and a kill-switch make the blast radius one client wide, not one agency wide. ## How it works 1. Create a child workspace per client — the surface they see carries your brand. 2. Run the book from the roll-up console: every child's activity and status in one place. 3. Set per-child caps, so one client's runaway campaign stops at their limit instead of consuming yours. 4. Hold the kill-switch: suspend a child instantly when a contract, an incident, or an unpaid invoice says so. Rebilling — charging your clients through the platform — arms with billing at launch. Until then, we bill you and you invoice your clients. ## The evidence :::panel The control set, per child - A workspace under your brand — your client's view of the product is yours - Sending caps — enforced per child, set by you - A kill-switch — instant suspension, held by the parent - The roll-up console — the whole book on one screen - Rebilling — arms with billing at launch ::: ## Honest limits :::tradeoffs Reputation has no white label Branding changes what clients see, not how mailbox providers judge mail. Sending reputation is earned per domain and per stream, and a client sending garbage will feel it regardless of whose logo is on the dashboard. Caps and the kill-switch contain the damage; they don't repeal the physics. And rebilling waits for billing to arm at launch — plan your invoicing accordingly. ::: ## Where to go next Volume terms live on [pricing](/pricing). Many agencies run client [newsletters](/features/newsletters) under this structure, and the surrounding controls — identity, audit, custody — are behind the [enterprise door](/enterprise). When a client asks how we stack up against the platform they're on, the [comparison hub](/compare) has the honest answers. --- # Journeys that survive crashes, deploys, and success > Email automation journeys on a durable, versioned, crash-safe engine: visual canvas, real-time event entry, A/B testing, and send-time optimization. ## Automation that doesn't lose people Email automation journeys here run on a durable, versioned, crash-safe engine: state persists outside any process, so a worker crash or a deploy resumes a journey instead of restarting or dropping it. The canvas is visual; the machinery underneath is what makes it trustworthy. ## How it works 1. Build the journey on the visual canvas — nodes and edges, readable by the non-engineers who will own it. 2. Enter contacts in real time from custom events: the CDP ingests events you send, and an event can trigger entry the moment it arrives. 3. Test variants with A/B splits whose winner statistics are peeking-safe — looking early doesn't corrupt the verdict. 4. Let send-time optimization schedule each recipient individually, at the hour their history says they read. Journeys are versioned: an edit creates a new version rather than silently rewriting what came before. ## The evidence :::panel Event in, journey entered ```json // POST /v1/events { "email": "ada@example.com", "event": "trial_started", "properties": { "plan": "growth" } } ``` The event lands on the contact's CDP timeline and can enter them into a journey in real time — no polling loop, no nightly sync job to babysit. ::: ## Honest limits :::tradeoffs Two honest notes encrypted sends give up click-tracking, send-time optimization, and per-recipient analytics — that is what “we can't read it” costs, and we say so. For organizations with at-rest encryption enabled, that applies to journeys too: they run, but send-time optimization and per-recipient engagement analytics don't. Separately: peeking-safe statistics mean the engine won't declare an A/B winner early just because you looked — verdicts take as long as the math requires, and impatience doesn't shorten it. ::: ## Where to go next Journeys pair with [newsletters](/features/newsletters) on the content side and feed on the same events you can receive over [webhooks](/features/webhooks). The [creator door](/creators) shows where automation fits the bigger loop, and if you're coming from a creator suite, the [Kit comparison](/compare/kit-alternative) covers what changes and what you'd give up. --- # Obligations as code paths, not PDFs > A GDPR email API posture built in code: crypto-shred erasure, retention windows, consent and DPA tracking, EU residency tagging, and legal hold. ## Compliance machinery, not compliance theater For teams that need a GDPR email API, the obligations here are mechanical: erasure, retention, consent, audit, and hold are code paths with defined behavior, not policies asserted in a document and hoped about in production. ## How it works | Obligation | Mechanism | |---|---| | Erasure | GDPR erasure by crypto-shred: destroying a key destroys the data it protected, without corrupting the tamper-evident audit ledger | | Retention | Retention windows that age data out on schedule | | Consent | Consent records tracked in-product, alongside DPA and subprocessor tracking | | Residency | Data residency tagging — EU today | | Audit | Tamper-evident hash-chained audit trails | | Legal hold | Supersedes erasure while active; normal rules resume when it lifts | Operator access is part of the record too: a vendor-access transparency log: a tamper-evident chain that records operator access, so “we never looked” is checkable, not promised. ## The evidence :::panel The erasure/audit conflict, resolved Erasure and auditability pull in opposite directions: one wants data gone, the other wants history intact. Crypto-shred resolves the conflict — destroying a key destroys the data it protected, while the audit chain keeps its integrity. You get a provable "it's gone" and a provable "here's what happened," at the same time, about the same events. ::: ## Honest limits :::tradeoffs Scope, stated plainly Residency tagging covers the EU today — we won't sell you regions that don't exist yet. Legal hold beats erasure only while a hold is active, and that precedence is documented rather than discovered. And no feature list makes you compliant: these are mechanisms your process can rely on, not a substitute for having one. ::: ## Where to go next The contractual side lives in the [DPA](/legal/dpa) and the [privacy policy](/legal/privacy). Erasure and custody rest on the [encryption layer](/features/encryption); the [enterprise door](/enterprise) has the complete control picture. Comparing compliance-focused vendors? The [Paubox comparison](/compare/paubox-alternative) draws the boundary honestly. --- # Deliverability as engineering, not folklore > An email deliverability platform on pipes we own: health-gated warmup, a reputation circuit breaker, VERP bounce classification, and per-tenant fair queueing. ## The parts, and how they connect An email deliverability platform earns trust with mechanisms you can name and inspect. Start with the foundation: Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. Everything below builds on owning that path. ## How it works | Mechanism | What it does | |---|---| | IP warmup | Health-gated ramp per receiver group — volume grows only on green signals. Arms with live sending | | Reputation circuit breaker | Throttles or suspends a deteriorating stream before mailbox providers do it for you | | Blocklist monitoring | Watches listings and shifts traffic off a listed IP automatically. Arms at launch | | Bounce classification | VERP-based; hard, soft, or block — and a policy block never suppresses a deliverable address | | Complaint loops | FBL reports become instant suppressions. Arms at launch | | Report ingestion | DMARC aggregate and TLS-RPT reports, parsed and surfaced | | Fair queueing | Per-tenant — one tenant's blast can't delay another tenant's password reset | Every send is inspectable individually — the [delivery inspector](/features/observability) reconstructs any message's full story. ## Proof and protocol hygiene Two details that punch above their weight: one-click unsubscribe per RFC 8058, with the headers signed under DKIM so stripping them in transit breaks the signature. And when a delivery needs to be provable rather than just observable: delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. ## The evidence :::panel Why "block" is its own bounce class ``` 550 5.7.1 Service unavailable; client [203.0.113.7] blocked ``` That's a policy block — a statement about the sending IP's standing, not the address's validity. Classify it "hard" and you delete a deliverable subscriber. We classify it `block`, fix the standing, and the address survives. Misclassification here is how platforms quietly shred good lists. ::: ## Honest limits :::tradeoffs No numbers without history The pieces that need live traffic — warmup, complaint loops, blocklist shift-off — arm at launch, and we publish no delivery-performance figures until real history exists to back them. Also true: no engine rescues bad sending. A purchased list or a surprise blast will hurt on our infrastructure too. The machinery makes good sending compound; it doesn't make bad sending safe. ::: ## Where to go next Background reading: [what IP warmup is](/glossary/ip-warmup) and [how DMARC works](/glossary/dmarc). For day-to-day sending practice, start at the [creator door](/creators); for the guarantees and controls around delivery, the [enterprise door](/enterprise). Leaving an incumbent over deliverability answers? The [SendGrid comparison](/compare/sendgrid-alternative) is where to start. --- # The last gate before anything leaves > An outbound email DLP gateway that fails closed: detect card numbers, SSNs, and secrets, then block, redact, or hold for a second person's sign-off. ## Outbound mail, inspected on the way out Email DLP here is an enforcement point, not an after-the-fact report: an outbound data-loss-prevention gateway that fails closed — card numbers, government IDs, and secrets can be blocked, redacted, or held for a second person's sign-off before anything leaves. ## How it works 1. Define a policy: which detectors — card numbers, government IDs, secrets and API keys — apply to which mail. 2. Choose the action per rule: block outright, redact the match and send, or require TLS for delivery and hold the message when that can't be assured. 3. Held messages need a second person. Maker-checker release means the sender can't approve their own exception. 4. Evaluation happens twice: at admission and again at send time, so nothing that changed in between — a template edit, a variable value — can slip past the first check. The failure behavior is the headline: an evaluator error blocks rather than leaks. And until you define a policy, the gateway is inert — no latency tax, no surprise blocks. ## The evidence :::panel Two checkpoints, both fail-closed ``` admission --[DLP evaluate]--> durable queue --[DLP re-evaluate]--> send error? block error? block ``` The second evaluation exists because time passes between accepting a message and sending it. Whatever changed in that window faces the policy again before anything leaves. ::: ## Honest limits :::tradeoffs Detection is patterns, and patterns have edges Detectors match structure, and structure has lookalikes — a sixteen-digit order number can trip a card-number check, so expect a tuning period with false positives. Maker-checker adds human latency to held mail by design; the pause is the control functioning. And fail-closed means an evaluator bug stops your mail instead of leaking your data — availability traded for containment, deliberately. ::: ## Where to go next DLP composes with [customer-held keys](/features/encryption) — one governs what leaves, the other who can read what's stored — and with the [compliance machinery](/features/compliance) around both. The [enterprise door](/enterprise) shows the full control set, and if you're weighing security-first vendors, the [Paubox comparison](/compare/paubox-alternative) is the relevant one. --- # One POST, one gate, one send that can't be lost > The transactional email API with one admission gate: durable 202 sends, idempotency keys, batches of 500, scheduling, suppressions, and validation. ## A send call that holds up under failure Email Fast is a transactional email API with a single write path. You call `POST /v1/emails`, and a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. Everything else on this page — batches, scheduling, streams, suppressions, validation — hangs off that one call and passes through that one gate. ## How it works | Surface | What you get | |---|---| | `POST /v1/emails` | One message. `idempotency_key` makes retries safe; `send_at` schedules up to 30 days out; a scheduled send can be canceled before it leaves | | `POST /v1/emails/batch` | Up to 500 messages per call, with a per-item result for each | | Streams | Separate transactional traffic from broadcasts, so one kind's volume never sits in front of the other | | `GET /v1/messages/:id` | The full timeline of any send: admission, render, queue, delivery events | | Suppressions | Bounces, complaints, and unsubscribes enforced at admission — readable and manageable over the API | | `POST /v1/validate` | Catch undeliverable addresses before you spend a send on them | Underneath all of it: every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. ## The evidence :::panel What a 202 actually commits to ```json // POST /v1/emails { "to": "ada@example.com", "subject": "Reset your password", "html": "

Your reset link is inside.

", "idempotency_key": "reset-8412" } // HTTP/1.1 202 Accepted { "id": "msg_01h9...", "status": "queued" } ``` Replay that exact request — same `idempotency_key` — and you get the same `msg_01h9...` back, not a second email. Kill the process right after the 202 and the send still happens: it was on disk before we answered. ::: ## Honest limits :::tradeoffs What a 202 is not A `202` is admission, not delivery. Delivery is asynchronous; the outcome arrives on the message timeline and over [webhooks](/features/webhooks). The caps are real: 500 per batch, 30 days for `send_at` — guardrails, not upsells. And during early access, live outbound delivery opens at launch; everything here runs today against [sandbox keys](/features/sandbox). ::: ## Where to go next Start with the [developer overview](/developers) or go straight to the [API reference](/docs). Coming from another provider? Your existing SDK may already work — the [comparison](/compare/sendgrid-alternative) covers the differences that matter. --- # Your keys, held by you — with a revoke that means it > BYOK email encryption with an instant fail-closed revoke, at-rest message encryption under keys you hold, crypto-shred erasure, and a vendor-access log. ## Custody, not ceremony BYOK email encryption here means custody with consequences: organizations can bring their own encryption keys — enroll, rotate, suspend, or revoke — and revocation fails closed: new sends are rejected and stored secrets become unreadable, to us included. ## How it works 1. Enroll a key and manage its lifecycle: rotate on your schedule, suspend for a reversible pause, revoke to end it — terminally. 2. Optionally turn it on for your organization: BYOK organizations can additionally enable at-rest message encryption: recipient, subject, and body stored as ciphertext under per-recipient keys wrapped by the customer's key — revoke the key and the stored content is unreadable everywhere, instantly. 3. Erasure follows custody: GDPR erasure by crypto-shred: destroying a key destroys the data it protected, without corrupting the tamper-evident audit ledger. 4. And access stays accountable: a vendor-access transparency log: a tamper-evident chain that records operator access, so “we never looked” is checkable, not promised. ## The evidence :::panel The lifecycle, spelled out ``` enroll -> rotate (repeatable) -> suspend (reversible) -> revoke (terminal) ``` Revocation propagates cross-process immediately: new sends for the organization are rejected and stored secrets become unreadable, to us included. There is no grace window in which "revoked" means "mostly revoked." ::: ## Honest limits :::tradeoffs What "we can't read it" costs encrypted sends give up click-tracking, send-time optimization, and per-recipient analytics — that is what “we can't read it” costs, and we say so. And the kill-switch cuts both ways: revoke a key and the stored ciphertext is gone as data — everywhere, instantly, unrecoverable by anyone including us. That finality is the security property. Treat revocation with the gravity it was built to have. ::: ## Where to go next At-rest encryption pairs naturally with [proof of delivery](/features/proof-of-delivery) — evidence without exposure — and with the [compliance machinery](/features/compliance) for erasure and retention. Our full posture and disclosure policy live on the [security page](/security); the [enterprise door](/enterprise) has the wider picture. For how customer-held custody differs from a compliance-first provider's, see the [Paubox comparison](/compare/paubox-alternative). --- # Signup forms that produce provable consent > Hosted double opt-in signup forms with consent records that hold up, a preference center with topic opt-down, and confirmation through our own delivery path. ## Consent you can show, not just claim Strictness is the feature: hosted signup forms with double opt-in through our own delivery path, so every subscriber carries verifiable proof of consent. Host them at `/f/:slug` with zero frontend work, or embed them in your site. ## How it works 1. Create a double opt-in signup form; share the hosted page at `/f/:slug` or embed it. 2. A visitor submits and receives a confirmation email — sent through the same delivery path as everything else, not a side channel. 3. They confirm at `/c/:token`. Only then do they become a subscriber, with a consent record capturing the signup and the confirmation. 4. Every subscriber gets a preference center at `/p/:token`: subscription topics and opt-down choices, so leaving entirely isn't the only exit. An optional captcha can be required per form, and it fails closed: until the captcha provider is armed at launch, a form that requires one refuses submissions instead of accepting them unverified. ## The evidence :::panel The consent chain ``` POST /f/:slug visitor submits | v confirmation email (our own delivery path) GET /c/:token visitor confirms | v subscriber active consent record: signup + confirmation, timestamped ``` No confirmation, no subscriber. The record exists because the process cannot complete without creating it. ::: ## Honest limits :::tradeoffs Double opt-in costs signups Some fraction of people never click the confirmation, and your list grows more slowly than it would on single opt-in. That's the trade, and we've picked a side: a smaller list with provable consent is worth more than a bigger one you can't defend. Also plainly: the captcha option arms at launch, and until then a form requiring it fails closed rather than open. ::: ## Where to go next Forms feed [newsletters](/features/newsletters) directly, and the [creator door](/creators) shows the whole audience-building loop. Collecting messages rather than subscribers — a contact form? That's the [browser SDK](/features/frontend). Leaving a suite that bundles forms with contact-tier pricing? See the [Mailchimp comparison](/compare/mailchimp-alternative). --- # Send from the browser without an abusable secret > Send email from your frontend with a browser SDK that can't be abused: the recipient always comes from a server-stored template, never the request. ## The contact-form problem, solved structurally You can send email from frontend code with `@email-fast/browser` and no backend at all. The key you ship in the page is public by design and safe by structure: a browser SDK with EmailJS-compatible endpoints — the recipient always comes from the server-stored template, never from the request, so a public key in your frontend can't be abused to spam arbitrary addresses. Rate limiting is on top of the safety story here, not the whole of it. ## How it works 1. Create a template server-side. Its recipient — you, your support inbox — is fixed there. Nothing in a browser request can choose or change it. 2. Give the template a typed `variable_schema` (`string`, `email`, `number`, `boolean`, with `required`, `maxLen`, `enum`). Requests with unknown, missing, or oversized fields are rejected. 3. Allowlist your origins for the key and set per-key rate caps. 4. Optionally enable a visitor auto-reply — "we got your message" — capped per visitor per day. ## The evidence :::panel The whole request, EmailJS-shaped ```json // POST /api/v1.0/email/send { "user_id": "your_public_key", "service_id": "default", "template_id": "contact_form", "template_params": { "name": "Ada", "message": "Hello there" } } ``` Notice what's missing: a `to` field. There isn't one, and there is nothing to substitute for it — the recipient lives in the template, on the server. `template_params` is validated against the template's typed schema before admission. ::: ## Honest limits :::tradeoffs A deliberately narrow lane This lane is for contact forms, feedback, and lead capture — mail that comes to you. Arbitrary recipients require the [REST API](/features/email-api) and a server, on purpose. Origin allowlists stop browsers, not `curl` — any non-browser client can forge an `Origin` header — which is exactly why recipient pinning and per-key caps exist: the allowlist cuts noise, the structure prevents harm. And rate caps bind honest traffic spikes too; raise them deliberately, not reactively. ::: ## Where to go next Five-minute setup in the [browser quickstart](/docs/quickstart-browser). Collecting subscribers rather than messages? That's [signup forms](/features/forms). The [developer door](/developers) covers the rest of the surface, and the [comparison](/compare/emailjs-alternative) is the honest read if you're deciding whether to switch. --- # Enterprise identity, done the boring right way > SAML SSO and SCIM provisioning on enterprise plans, with WebAuthn passkeys, TOTP two-factor, recovery codes, and hardened session security for every account. ## Sign-in your security team won't fight Email Fast is an SSO and SCIM email platform on its enterprise plans: SAML single sign-on against your identity provider, and SCIM to provision and deprovision users automatically from your directory. Passkeys, TOTP, and hardened sessions aren't gated at all — every account gets them. ## How it works 1. SAML SSO — connect your identity provider; sign-in happens where your directory lives. Available on enterprise plans. 2. SCIM — provisioning and deprovisioning follow your directory, so offboarding removes access here the moment it happens there. Enterprise plans. 3. WebAuthn passkeys — phishing-resistant sign-in, on every account. 4. TOTP with recovery codes — standard authenticator-app two-factor, on every account. Underneath all of it, sessions are hardened: `__Host-`-prefixed cookies and CSRF protection. ## The evidence :::panel What the __Host- prefix buys Per the cookie-prefixes rules in the current cookie specification (RFC 6265bis), a browser refuses a `__Host-` cookie unless it is set with `Secure`, from a secure origin, with no `Domain` attribute, and `Path=/`. The session cookie therefore structurally cannot be scoped wider than this host or sent over plaintext — an invariant enforced by the browser itself, not by our configuration staying correct. ::: ## Honest limits :::tradeoffs Gating and lost-key reality SAML SSO and SCIM sit on enterprise plans — the same answer the pricing page gives, because a feature page and a pricing page that disagree are their own kind of security problem. Recovery codes are shown once; store them like the credential they are. And SAML setup requires an administrator on your identity provider's side — that half of the handshake is necessarily yours. ::: ## Where to go next The wider control set — audit chains, DLP, key custody — lives behind the [enterprise door](/enterprise). Our disclosure posture is on the [security page](/security), and the data-handling side is under [compliance](/features/compliance). Evaluating vendors side by side? Start at the [comparison hub](/compare). --- # Inbound mail becomes a webhook with a verdict > Receive email at your domain and get a full parse by webhook: routing by address, SPF, DKIM, and DMARC verdicts, and reply classification built in. ## From mailbox to webhook Inbound email parsing turns mail sent to your domain into a structured `email.inbound` webhook: the full parsed message, routed by the address it arrived at, with SPF, DKIM, and DMARC verdicts attached. Your code handles a JSON object, not MIME. ## How it works 1. Point mail for your domain — most teams start with a subdomain — at us, and define routes by address. 2. Each message is fully parsed and authenticated: SPF, DKIM, and DMARC verdicts ride along on the event. 3. Reply intelligence classifies what the message actually is: an out-of-office, an unsubscribe request written in prose, a complaint, or a genuine lead. 4. Per route, opt in to auto-honoring written unsubscribe requests — "please take me off this list" suppresses the sender without a human in the loop. Bounce, complaint, and report mailboxes are handled automatically — the postmaster plumbing every domain is supposed to have, without you building it. ## The evidence :::panel What your endpoint receives (abridged) ```json { "type": "email.inbound", "data": { "route": "support@yourdomain.com", "from": "ada@example.com", "subject": "Re: your invoice", "auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass" }, "classification": "genuine_lead" } } ``` The full parsed content rides along too — the abridgment here is ours, not the payload's. ::: ## Honest limits :::tradeoffs Classification is a judgment, not a fact The classifier reads prose, and prose is messy: expect occasional misreads. That's why auto-honoring unsubscribe requests is opt-in per route, and why the complete parsed message always accompanies the classification — your handler can overrule it. Authentication verdicts are reported, not enforced: a DMARC `fail` is attached for your logic to act on, because silently dropping mail is how support threads vanish. ::: ## Where to go next Inbound events arrive over the same [signed webhooks](/features/webhooks) as outbound events. Classified replies can drive [automations](/features/automation) — a genuine lead enters a journey, an out-of-office doesn't. The full payload reference lives behind the [developer door](/developers), and if you run inbound routes on Mailgun today, the [comparison](/compare/mailgun-alternative) covers the switch. --- # Keep your SDK. Change the base URL and the key. > SendGrid, Mailgun, and Postmark compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code. Importers arm at launch. ## Your existing code is the migration The rewrite is the step you skip: SendGrid-, Mailgun-, and Postmark-compatible endpoints: point your existing SDK at a new base URL with a new key and keep your code. That's what a SendGrid compatible API is for — with Mailgun and Postmark equivalents beside it. The switch is configuration: new base URL, new key, same code paths you've already debugged in production. ## How it works | Coming from | Endpoint | Auth and semantics | |---|---|---| | SendGrid | `POST /v3/mail/send` | `Authorization: Bearer`; SendGrid-shaped `202` with an `X-Message-Id` response header | | Mailgun | `POST /v3/:domain/messages` | HTTP Basic `api:key`; urlencoded and multipart bodies both accepted | | Postmark | `POST /email` and `POST /email/batch` | `X-Postmark-Server-Token` header; Postmark error semantics, including error code `406` for an inactive recipient | Every compat request funnels into the same checkpoint as native traffic — every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. And batch calls through the compat surface are transactionally atomic: a mid-batch failure rolls back every recipient in the call, so retries can't double-send. ## The evidence :::panel A SendGrid-shaped send, verbatim ```bash curl https://api.emailfast.dev/v3/mail/send \ -H "Authorization: Bearer $EMAILFAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{"personalizations":[{"to":[{"email":"ada@example.com"}]}], "from":{"email":"you@yourdomain.com"}, "subject":"Hello", "content":[{"type":"text/html","value":"

Hi.

"}]}' ``` Response: `202` with `X-Message-Id` — exactly where your existing error handling expects to find them. ::: ## Honest limits :::tradeoffs Compatibility, scoped honestly We mirror the send surfaces your code actually calls, not three vendors' entire product APIs — their campaign-management and statistics endpoints are not cloned. Template, contact, and suppression importers arm at launch; until then compatibility moves your code, and your data follows. Where behavior differs, the per-provider guides say so plainly. ::: ## Where to go next Each guide has the concrete path for its provider: [SendGrid](/migrate/sendgrid), [Mailgun](/migrate/mailgun), [Postmark](/migrate/postmark), and [EmailJS](/migrate/emailjs). Still deciding whether to switch at all? The [comparison](/compare/sendgrid-alternative) is the honest version, including where staying put is the right call. --- # The publication is the product: archive, feed, and inbox > A newsletter platform with RSS, a public web archive, hosted subscribe pages, and signed view-in-browser links. Paid subscriptions arm at launch. ## More than a blast tool Email Fast is a newsletter platform with RSS and a public web presence built in: every publication gets a hosted subscribe page, a search-indexable archive at `/n/:pub`, a page per issue, an RSS feed, and signed view-in-browser links. Under it sits a pricing posture worth naming: contacts and subscribers are never billed — we price sending, not the size of your audience. ## How it works 1. Create a publication and set up its template once. 2. Write and send issues. Each one reaches subscriber inboxes and, at the same time, lands on the public archive and the RSS feed. 3. Readers find back-issues through search or the feed, and subscribe through the hosted page or an [embedded form](/features/forms). 4. Every issue carries a signed view-in-browser link — usable without a login, not guessable. Two pieces arm at launch: paid subscriptions (reader billing through Stripe) and RSS-to-email, which turns a feed you already publish into issues automatically. ## The evidence :::panel What every publication ships with - A hosted subscribe page — zero frontend work - A public, search-indexable archive at `/n/:pub`, with a page per issue - An RSS feed of the archive - Signed view-in-browser links on every send - Paid subscriptions via Stripe — arms at launch ::: Who carries the mail matters more here than anywhere: every newsletter platform we surveyed rents its delivery: beehiiv's status page lists SendGrid components; Buttondown's subprocessor list names Mailgun and Postmark; Substack sends through Mailgun (verified on their own public pages, July 2026). Email Fast is the other kind of company — Email Fast runs its own mail transfer agent, warmup engine, reputation breaker, and per-tenant fair queue — the pipes are ours, not resold. ## Honest limits :::tradeoffs Public by design, and two things that wait The archive is the point: issues are public and search-indexable, which is how new readers find you — so don't publish what shouldn't be on the open web. Paid subscriptions and RSS-to-email both arm at launch, with billing. And subscribers acquired through our forms carry double opt-in confirmation, which costs some signups; a list you can defend is the trade we've picked. ::: ## Where to go next The [creator door](/creators) has the whole story — audience ownership, exports, honest metrics. Pair publications with [signup forms](/features/forms) and [automations](/features/automation). Leaving a hosted newsletter platform? The [comparison](/compare/substack-alternative) is honest in both directions. --- # Every message can tell you its whole story > A per-message email delivery inspector: full timelines, remote-MX transcripts on the managed lane, and signed, expiring share links for any delivery story. ## From "it didn't arrive" to a named cause The Message Explorer is an email delivery inspector: pick any send and read its full story — admission, render, queue, each delivery attempt, and the outcome — in one timeline. ## How it works 1. Find the message in the Explorer and read its timeline end to end. 2. On the managed lane, the Delivery Inspector adds the remote-MX transcript: TLS negotiated, deferrals, the receiving server's verbatim responses, per-hop latency. Full transcript depth arms at launch. 3. Share the story: `/s/:token` links are signed, expiring, and revocable — a redacted, read-only delivery story you can hand to a customer, an auditor, or a receiving postmaster. 4. Ask the deliverability copilot. It's a deterministic diagnosis engine: the same evidence yields the same, named diagnosis, and when the signals don't support a conclusion it says so instead of guessing. Internally, the platform runs on full Prometheus instrumentation — every subsystem measured, because owned delivery infrastructure can't be operated blind. ## The evidence :::panel A delivery story, abridged ``` 09:14:03 admitted idempotency ok, suppression clear, policy pass 09:14:04 rendered template v7, 41 KB html 09:14:05 attempt 1 mx2.example-isp.com — deferred 421 4.7.28 09:16:19 attempt 2 accepted — 250 2.0.0 OK; TLSv1.3 ``` And when a story needs to be proof rather than observability: delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. ::: ## Honest limits :::tradeoffs Depth arms at launch, and links are power Full remote-transcript depth on the managed lane arms at launch with the live sending path — the timeline works today; the deepest wire detail follows. Share links are redacted and read-only, but a link is still access: they expire and you can revoke them, and both properties exist because someone will eventually share one too widely. The copilot names only what the evidence supports — "unknown" is an answer it's allowed to give. ::: ## Where to go next The inspector shows you what the [deliverability engine](/features/deliverability) is doing; [proof of delivery](/features/proof-of-delivery) turns a story into a portable certificate; the [developer door](/developers) covers the API surface it all hangs off. Postmark's public delivery board is the incumbent benchmark here — the [comparison](/compare/postmark-alternative) explains what we offer instead. --- # Delivery you can prove to someone who doesn't trust you > Proof of email delivery you can verify without trusting us: Ed25519-signed certificates with the receiving MX, TLS details, and the verbatim SMTP response. ## What we can prove, we sign Proof of email delivery here is cryptographic, not a screenshot of a dashboard: delivered messages can mint an Ed25519-signed delivery certificate — receiving mail server, TLS details, the server's SMTP response, and timestamps, with the recipient stored only as a keyed hash — chained into a tamper-evident ledger and verifiable without trusting us. ## How it works 1. A delivered message can mint a certificate recording the receiving MX, the TLS protocol and cipher of the session, the receiving server's verbatim SMTP response, and timestamps — with the recipient stored only as a keyed hash, so the certificate itself is PII-free. 2. Each certificate is appended to a hash-chained WORM ledger: write-once, append-only. History can be extended, never edited. 3. Anyone can verify a certificate against our published public key — offline, independently. 4. Bulk export is one call: `GET /v1/certificates/export`. Legal hold is built in and supersedes erasure while active: held certificates survive deletion requests for as long as litigation requires, and normal erasure resumes when the hold lifts. ## The evidence :::panel A certificate, abridged ```json { "recipient": "keyed-hash:9f2c...", "mx": "aspmx.l.google.com", "tls": { "protocol": "TLSv1.3", "cipher": "TLS_AES_256_GCM_SHA384" }, "smtp_response": "250 2.0.0 OK 1752741203 gsmtp", "accepted_at": "2026-07-17T09:14:03Z", "signature": "ed25519:...", "prev": "sha256:..." } ``` The `prev` hash chains it into the ledger; the signature binds the contents; the keyed hash keeps the recipient's address out of the artifact entirely. ::: ## Honest limits :::tradeoffs What a certificate cannot prove It proves the receiving server accepted the message over the recorded TLS session and said so in the quoted response. It does not prove a human read it, and it cannot prove which folder the message landed in — no email protocol offers either, and we won't pretend otherwise. A `250` acceptance is the strongest artifact email can produce; this makes that artifact portable, signed, and independently checkable. ::: ## Where to go next Certificates pair with [customer-held keys and at-rest encryption](/features/encryption) — evidence without exposure — and with the wider [compliance machinery](/features/compliance) for retention, erasure, and hold. The [enterprise door](/enterprise) frames where this fits; evaluating secure-email vendors? See the [comparison](/compare/paubox-alternative). --- # Test against the real pipeline, dry > An email testing sandbox that runs the real pipeline dry: real validation, rendering, and events, a hosted capture inbox, and nothing ever leaves. ## A test mode that isn't a mock Most email testing sandbox setups stub the interesting parts. Ours doesn't: sandbox keys (ef_sandbox_…) that run the real pipeline dry: real validation, real rendering, real events, a hosted capture inbox — and no email leaves. It's free and unlimited on every plan, and it's live today. ## How it works 1. Create a key with the `ef_sandbox_` prefix. The prefix is the mode — there's no separate environment to configure. 2. Point anything at it: REST sends, batches, templates, the browser SDK. Requests run the entire pipeline — validation, rendering, event generation — and stop at the wire. 3. Open the hosted capture inbox and see each message exactly as it would have left. 4. Assert on the event stream or webhook deliveries in CI — the same event shapes production emits. Sandbox rows never touch production reputation ledgers. Test as loudly as you like; warmup schedules and reputation stats don't hear it. ## The evidence :::panel Same call, sandbox key ```bash curl https://api.emailfast.dev/v1/emails \ -H "Authorization: Bearer ef_sandbox_..." \ -d '{ "to": "test@example.com", "subject": "CI run 1847", "html": "

ok

" }' ``` A real `202`, a real message id, a real timeline — and the message lands in the capture inbox instead of the internet. ::: ## Honest limits :::tradeoffs Dry means dry The sandbox proves your integration, not your reputation. There is no remote mail server in the loop, so delivery outcomes are simulated, and it can't tell you how a real mailbox provider will treat your domain — those questions need live sending, which opens at launch. It also isn't a rendering matrix: the capture inbox shows the compiled message, not screenshots across twenty clients. ::: ## Where to go next Wire it into a test suite with the [Node quickstart](/docs/quickstart-node), then browse the [full API surface](/features/email-api) it exercises and the [webhooks](/features/webhooks) it feeds. If you currently pay for a separate service just to test email, the [comparison](/compare/mailgun-alternative) is worth five minutes. --- # Change a config file, not your codebase > An SMTP relay on ports 587 and 465 where the password is your API key. Every recipient passes the same admission gate as the REST API. Arms at launch. ## The relay for code you'd rather not touch If your application already speaks SMTP, Email Fast is an SMTP relay service you adopt by editing configuration. Connect on port 587 (STARTTLS) or 465 (implicit TLS), authenticate with your API key as the SMTP password, and every message enters the exact pipeline the REST API uses. The relay arms at launch, when outbound delivery opens. ## How it works 1. Point your app at the relay: port `587` (STARTTLS) or `465` (TLS from the first byte). 2. Authenticate. The SMTP password is your API key — no second credential to provision, rotate, or forget. Revoke the key and the relay stops with it. 3. Send. Each `RCPT TO` is admitted individually — suppression, quota, and content policy checked per recipient, exactly as a REST call would be. 4. Watch results the same way as any other send: the [message timeline](/features/email-api) and signed webhooks. One property worth naming plainly: the server refuses `AUTH` before TLS is established. And behind the socket, every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip. ## The evidence :::panel A session on port 587 ``` 220 smtp.emailfast.dev ESMTP EHLO app.example.com 250-STARTTLS AUTH LOGIN 530 5.7.0 Must issue a STARTTLS command first STARTTLS 220 2.0.0 Ready to start TLS [TLS established] AUTH LOGIN 235 2.7.0 Authentication successful ``` The refused `AUTH` is the point: sending credentials in cleartext is structurally impossible, not merely discouraged. ::: ## Honest limits :::tradeoffs SMTP is the narrow interface The protocol has no verbs for idempotency keys, `send_at` scheduling, or per-item batch results — those are [REST conveniences](/features/email-api) one endpoint away when you want them. Per-recipient admission also means partial acceptance is normal: a suppressed address is refused while the rest of the envelope goes through. And the relay itself arms at launch — it needs the live outbound path to mean anything. ::: ## Where to go next Connection details and key management live in the [developer docs](/developers). If you're moving off another provider wholesale, the [drop-in migration endpoints](/features/migration) go further than SMTP alone — and the [comparison](/compare/mailgun-alternative) shows honestly where we differ. --- # Templates that compile once and can't be injected > Email templates in Handlebars and MJML with versioning, typed variable schemas, and a compile-time lint that makes HTML injection structurally impossible. ## Author once, render safely forever Write MJML email templates with Handlebars variables and stop hand-assembling table HTML. Templates are versioned, every variable is typed, and the compile-time lint rejects any unescaped output — so template HTML-injection is structurally impossible rather than merely discouraged. ## How it works 1. Author in MJML for layout, Handlebars for data. MJML compiles at author time — never per-send — so sending renders merges of prebuilt HTML, and a broken layout surfaces when you save, not mid-campaign. 2. Declare a typed schema per template: `string`, `email`, `number`, `boolean`, with `required`, `maxLen`, and `enum` constraints. A send whose `data` doesn't match is refused at admission. 3. Every edit creates a version instead of silently rewriting what came before. 4. The lint runs at compile time and rejects every unescaped output. There is no "just this once" escape hatch — that absence is what makes the guarantee structural. The editor also warns at author time when compiled HTML approaches Gmail's 102KB clipping threshold, while shortening it is still cheap. ## The evidence :::panel A typed variable schema ```json { "name": { "type": "string", "required": true, "maxLen": 80 }, "email": { "type": "email", "required": true }, "seats": { "type": "number" }, "plan": { "type": "string", "enum": ["free", "starter", "growth"] } } ``` Four fields, four types, hard limits. A payload of `{ "plan": "