# opencoredev/email-sdk: one TypeScript client for 24 transactional email adapters

> A server-side SDK that puts Resend, Postmark, SendGrid, SES and nineteen other providers behind one normalized message, with retries, fallback routes and fail-fast field checks. Here is how the mechanism works, where it stops, and whether your team should adopt it.

**opencoredev/email-sdk** — One simple SDK for transactional email across 23 adapters

- Repository: https://github.com/opencoredev/email-sdk
- Website: https://email-sdk.dev
- Stars: 494 · Forks: 25
- Language: MDX
- License: MIT
- Published: 2026-09-20 · Updated: 2026-09-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/opencoredev-email-sdk

## What provider lock-in actually costs a TypeScript backend

Most transactional email code is a thin wrapper around one vendor's HTTP API, which is fine until the vendor changes pricing, degrades deliverability for your sending domain, or gets acquired. At that point the migration cost is not the API call. It is every place in your codebase that learned the vendor's shape: the field names for tags, the way scheduled sends are expressed, the error codes you branch on, the retry behaviour you hand-rolled because the vendor's client does not do it.

opencoredev/email-sdk attacks that by normalizing the message. The README describes the core as "one normalized message" behind "adapters for 23 provider APIs plus SMTP, 24 adapters total". Each adapter lives at its own entry point, so the surface you import is only the provider you actually send through. The intended audience is a TypeScript or JavaScript backend team that already has provider accounts and wants the provider to be a configuration decision rather than an architectural one. This is not a sending service. You bring the account and the API key.

## How the adapter layer, retries and fallback routes fit together

The client is constructed with an array of adapters. In the README's example that array holds a single Resend adapter built from an API key, and every send goes through it. The normalization happens between your send call and the provider: you write one message object, and the adapter translates it into whatever the provider expects.

The interesting part is what happens when a send does not succeed. The README lists two distinct mechanisms: "Retries within an adapter, plus fallback routes across adapters". Those are different failure responses. A retry assumes the same provider will work on a second attempt, which covers transient network and rate-limit conditions. A fallback route assumes the provider will not work and moves the message to a different adapter. The docs link for this is the concepts page on fallbacks and retries, which is where the actual ordering and conditions live; the README does not spell out the policy, so treat the linked page as the source of truth before you depend on a specific behaviour.

The third mechanism is the one that saves the most debugging time. The README describes "Fail-fast field-support checks before a provider drops data". Providers silently ignore fields they do not support, so a tag or a scheduled-send timestamp can vanish and you find out from a customer. The SDK checks support up front and refuses the send instead. That is a deliberate trade: a loud failure in your logs rather than a quiet loss in production.

## Install and a first real send with the Resend adapter

The README gives the install as a single npm command and states the SDK is server-side only, requiring Node 20+ or Bun. It also warns to keep provider API keys out of client code, which matters because bundlers will happily inline an environment variable that reaches the browser.

```bash
npm install @opencoredev/email-sdk
```

The first send uses the Resend adapter from its own entry point. The README calls Resend the fastest first send for newcomers.

```ts
import { createEmailClient } from "@opencoredev/email-sdk";
import { resend } from "@opencoredev/email-sdk/resend";

const email = createEmailClient({
  adapters: [resend({ apiKey: process.env.RESEND_API_KEY! })],
});

await email.send({
  from: "Acme <hello@acme.com>",
  to: "user@example.com",
  subject: "Welcome",
  html: "<p>It works.</p>",
});
```

What you should see is a resolved promise when the provider accepts the message. The README notes that provider acceptance is not proof of delivery, so do not read a successful send as a delivered email.

Before trusting a new adapter, the CLI has a setup check. The README shows the doctor command with an adapter flag, and notes that doctor --live authenticates without sending. That distinction is worth using: a live check proves the key and account work without putting a message in someone's inbox.

```bash
npx --package @opencoredev/email-sdk email-sdk doctor --adapter resend
```

## Where the SDK stops: the cases that should send you elsewhere

The adapter list is long but it is not a promise of parity. Field support varies by provider, and the SDK's own answer to that is to fail fast rather than paper over the difference. If your message sets a field that your chosen provider does not accept, the send is refused. That is the right default for a transactional pipeline, and it is also a real constraint: moving from one adapter to another may require changing the message, not just the adapter import. The field-support page linked from the README is where you check that before a migration, not after.

The second boundary is the runtime. Node 20+ or Bun, server-side only. If you are deploying to an edge runtime that does not meet that bar, or you want to send from a browser, this is the wrong tool.

The third is scope. This is transactional email plumbing. The README mentions batch personalization with per-recipient variables and provider-side scheduled sends, but nothing about campaign management, contact lists, unsubscribe handling or inbound parsing. A team that needs those should keep them in a separate system rather than expect the SDK to grow into them.

One more thing to weigh honestly: the SDK collects anonymous usage analytics on first run and prints a notice about it. The README is specific about what is collected (adapter and CLI command names, send outcomes, recipient counts, attachment booleans, SDK version, OS, Node version, CI provider) and equally specific that email content, subjects, addresses, headers, attachments and API keys are never collected. It is off with EMAIL_SDK_TELEMETRY=0, DO_NOT_TRACK=1, per client with telemetry: false, or automatically under NODE_ENV=test. If your organisation cannot accept outbound analytics from a library at all, you have a one-line answer, but you have to make that decision deliberately.

## How this differs from Nodemailer and from a single-vendor SDK

Nodemailer is the obvious comparison and it is in the repository's own topics list. Nodemailer is an SMTP client with transport plugins; it is mature, it is not opinionated about which provider sits behind SMTP, and it does not know what fields a given provider supports. If your only requirement is "send this over SMTP", Nodemailer is a smaller dependency and a well-trodden path.

The difference here is the provider API layer. opencoredev/email-sdk talks to HTTP APIs directly for 23 providers, with SMTP as one more adapter among 24. That buys two things Nodemailer cannot give you: provider-specific capabilities such as provider-side scheduled sends, and the field-support check, which only exists because the SDK knows each provider's schema. It costs you the SMTP abstraction's universality. A provider without an adapter here is reachable through the SMTP adapter, but only with what SMTP can express.

Against a single-vendor SDK, the difference is the fallback route. A Resend SDK sends through Resend, and a Resend outage is your outage. Here, the adapter array and the fallback mechanism are the escape hatch, at the price of an extra layer between your code and the provider's own error semantics.

## Maintenance, licence and the cost of upgrading

The repository is not archived and the last push was on 2026-09-17, three days before this writing. Recent releases are close together: @opencoredev/email-sdk@1.2.0 on 2026-08-27, preceded by 1.1.0 on 2026-08-03, with a separate package, @opencoredev/convex-email@3.0.0, released the same day as 1.2.0. The version numbers are still in the 1.x range, which is the honest signal to read here: the core API is settling but has not been declared stable by a major version.

The monorepo is a Bun and Turborepo workspace with Changesets for versioning, and the release script chains type checks, tests, an adapter-verification check, a community registry check, docs version checks, a build and a package tarball check before publishing. There is also a script that plans live adapter checks against real accounts. That is a heavier release pipeline than most SDKs of this size carry, and it is aimed squarely at the risk this kind of project has: an adapter drifting out of sync with a provider's API.

The licence is MIT, which permits commercial use, modification and redistribution with the copyright notice and permission notice preserved. That is a permissive licence and it is compatible with keeping this in a closed-source product. Nothing here constitutes legal advice; if your organisation has a policy on bundled dependencies or on libraries that emit telemetry, route it through whoever owns that policy.

The upgrade cost is the adapter surface. Because each adapter is a separate entry point, a breaking change in one provider's adapter should not touch code that imports a different one. The thing to watch across upgrades is field support: a provider adding or removing a field changes what the fail-fast check accepts, and that can turn a working send into a refused one.

## Conclusion

Adopt it if you send transactional email from a Node 20+ or Bun server and you either run more than one provider or want the option to leave one without rewriting your send calls. Skip it if you need marketing automation, inbound parsing, or you are on a runtime the README excludes, and skip it if your only provider is one the adapter list does not name. Before you commit, run the doctor command against your real adapter, read the field-support page for the fields you actually set, and decide whether the anonymous telemetry is acceptable or must be turned off with EMAIL_SDK_TELEMETRY=0.

## FAQ

### Is there a free email API I can use with opencoredev/email-sdk?

The SDK is MIT licensed and free to install, but it is not an email service. It connects to provider accounts you already hold, and any free sending tier comes from that provider rather than from the SDK.

### Does opencoredev/email-sdk work in the browser?

No. The README states the SDK is server-side only and needs Node 20+ or Bun, and it warns to keep provider API keys out of client code.

### Which providers does opencoredev/email-sdk support?

The README lists adapters for 23 provider APIs plus SMTP, 24 adapters total, each imported from its own entry point. Resend, Postmark, SendGrid, AWS SES, Mailgun, Brevo, MailerSend, SparkPost, Mailchimp, Iterable, Loops, Plunk, Mailtrap, Cloudflare, Unosend, Scaleway, ZeptoMail, MailPace, Sequenzy, JetEmail, Lettermint, Lettr, Primitive and a testing adapter are named.

### How do I turn off telemetry in opencoredev/email-sdk?

Set EMAIL_SDK_TELEMETRY=0 or DO_NOT_TRACK=1 in the environment, or pass telemetry: false when calling createEmailClient. Telemetry is also disabled automatically when NODE_ENV=test.

### Can I check an adapter's setup without sending a real email?

Yes. The README gives the email-sdk doctor command with an --adapter flag, and notes that doctor --live authenticates without sending. The CLI also supports dry-run smoke sends.

## Sources

- [License: MIT](https://github.com/opencoredev/email-sdk/blob/main/LICENSE)
- [opencoredev/email-sdk on GitHub](https://github.com/opencoredev/email-sdk)
- [Project website](https://email-sdk.dev)
- [README](https://github.com/opencoredev/email-sdk/blob/main/README.md)
- [Releases](https://github.com/opencoredev/email-sdk/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/opencoredev-email-sdk
