JetEmail
Send through the JetEmail transactional API with CC, BCC, reply-to, custom headers, base64 attachments, and idempotent retries.
Capabilities
| Repeated headers | Idempotency | Scheduling | Personalized |
|---|---|---|---|
| No | native | No | expanded |
These values come from the adapter's exported capabilities declaration. The field support matrix covers normalized message fields.
The JetEmail adapter calls JetEmail's transactional POST /email endpoint with plain fetch. JetEmail runs its own sending infrastructure (anycast IPs, managed reputation), and the adapter maps the normalized EmailMessage straight onto its REST payload.
Configure
Create a JetEmail transactional API key (prefixed transactional_) in the JetEmail dashboard and verify the domain you send from.
import { createEmailClient } from "@opencoredev/email-sdk";
import { jetemail } from "@opencoredev/email-sdk/jetemail";
export const email = createEmailClient({
adapters: [jetemail({ apiKey: process.env.JETEMAIL_API_KEY! })],
});Prop
Type
Send
JetEmail accepts up to 50 recipients each across to, cc, and bcc, and maps headers to custom email headers. Attachments are base64-encoded automatically (40MB total).
const result = await email.send({
from: "Acme <hello@acme.com>",
to: [{ email: "user@example.com", name: "Ada" }],
cc: "team@acme.com",
replyTo: "support@acme.com",
subject: "Welcome to Acme",
html: "<p>Thanks for joining Acme.</p>",
text: "Thanks for joining Acme.",
});
console.log(result.id); // JetEmail message id, e.g. "19424fd2acd0004210"From requires a display name
JetEmail rejects a bare email in from; it requires the "Name <email>" form. The adapter
fails fast with an EmailValidationError when from has no display name, so pass
from: "Acme <hello@acme.com>" or from: { email: "hello@acme.com", name: "Acme" }.
No tags or metadata
JetEmail's send API has no tags or metadata field, so the adapter rejects tags and metadata
with an EmailValidationError before the request. Check field
support before using JetEmail in a fallback route.
Idempotent sends
Pass an idempotencyKey and the adapter forwards it as JetEmail's Idempotency-Key header, so a retried send with the same key and body replays the original response instead of sending twice.
await email.send(message, { idempotencyKey: "receipt:order_123" });Verify from the CLI
JETEMAIL_API_KEY="transactional_..." npx email-sdk doctor --adapter jetemailJETEMAIL_API_KEY="transactional_..." npx email-sdk send \
--adapter jetemail \
--from "Acme <hello@acme.com>" \
--to user@example.com \
--subject "JetEmail smoke test" \
--text "It works" \
--dry-runDrop --dry-run to send for real. The dry run already enforces JetEmail's local rules (the display-name requirement on from, the 50-address caps), so what a live send actually tests is the transactional_ key and whether your sending domain is verified on JetEmail's side.
