Quickstart#
Three steps to your first email.
In your workspace, open Settings → API keys and create a key. Store it as an environment variable.
emiit delivers through an email service. Here we use Resend, but any of the supported providers works.
npm install @emiit/sdk resendGrab a template ID from the dashboard and send. The full client setup is in the next section.
await emiit.send({
templateId: "tmpl_welcome",
from: "Emiit <hello@emiit.dev>",
to: "timi@emiit.dev",
subject: "Welcome to Emiit",
data: { name: "Jane" },
});Get your API key#
Open Settings → API keys in your workspace and create a key. It's the only thing you need to start sending, so keep it private and store it as an environment variable.
export EMIIT_API_KEY="emiit_sk_live_..."Send an email#
Pick a template, say who it's for, and emiit builds and sends the email. Use the Node SDK, or call the HTTP API from anything else.
import { Emiit } from "@emiit/sdk";
import { resend } from "@emiit/sdk/providers";
import { Resend } from "resend";
const emiit = new Emiit({
apiKey: process.env.EMIIT_API_KEY,
provider: resend(new Resend(process.env.RESEND_API_KEY)),
});
await emiit.send({
templateId: "tmpl_welcome",
from: "Emiit <hello@emiit.dev>",
to: "timi@emiit.dev",
subject: "Welcome to Emiit",
data: { name: "Jane", plan: "Pro" },
});You can send to one person or a whole list, add people on copy, set a reply-to address, or write the preview line. See sending options for everything you can include.
The Node client#
The SDK gives you typed options, readable errors, and no hand-written requests. Add it to your project:
npm install @emiit/sdkThese are the settings you give the client once, when you create it:
| Field | Type | What it does |
|---|---|---|
apiKeyrequired | string | Your emiit API key, from Settings → API keys. |
provideroptional | Provider | The email service that delivers the mail. Leave it out to send with emiit, from a domain you verified. |
fromoptional | string | A default sender, used when you don't pass one to send(). |
Set a default from on the client and you can leave it out of every send() call.
Delivery#
emiit renders your template; an email service delivers it. Plug in the one you already use with your own account and keys, so nothing about your sending reputation changes. These are the supported services:
resendnpm i resendnodemailernpm i nodemailer@sendgrid/mailnpm i @sendgrid/mailpostmarknpm i postmark@aws-sdk/client-sesnpm i @aws-sdk/client-sesYou set the service up with your own account details, then hand it to emiit. Here it is with Nodemailer over plain SMTP:
import { Emiit } from "@emiit/sdk";
import { nodemailer } from "@emiit/sdk/providers";
import nm from "nodemailer";
const transport = nm.createTransport({
host: "smtp.emiit.dev",
port: 587,
auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS },
});
const emiit = new Emiit({
apiKey: process.env.EMIIT_API_KEY,
provider: nodemailer(transport),
});Sending with emiit
Prefer not to bring a provider? Leave provider out and emiit delivers the mail itself. First connect the domain you want to send from:
Open Settings → Domains and add it. You'll get three DKIM CNAME records and a starter DMARC record.
Copy them into your DNS provider, then hit Check. Verification is usually minutes, though DNS can take up to 72 hours to propagate.
Once the domain reads verified, every address on it works as a from, subdomains included.
import { Emiit } from "@emiit/sdk";
const emiit = new Emiit({ apiKey: process.env.EMIIT_API_KEY });
await emiit.send({
templateId: "tmpl_welcome",
from: "Emiit <hello@emiit.dev>",
to: "timi@emiit.dev",
subject: "Welcome to Emiit",
data: { name: "Jane" },
});domain_not_verified rather than being delivered unsigned. Attachments still need a provider.Want to try it before touching DNS? Send from welcome@emiit.dev today. It needs no setup, but it only delivers to the email addresses of your workspace's members, so it's for testing rather than real traffic. Anything else comes back as recipient_not_allowed.
Sending options#
Everything you can include when you send an email, whether over the API or the SDK:
| Field | Type | What it does |
|---|---|---|
templateIdrequired | string | The ID of the template you built, shown on its page in the dashboard. |
torequired | string | string[] | One recipient, or a list of them. |
subjectrequired | string | The subject line. It can use variables too, like "Welcome, {{ name }}". |
fromrequired | string | Who the email comes from, like "Emiit <hello@emiit.dev>". Optional if you set a default from on the client. |
previewoptional | string | The short line shown next to the subject in the inbox. Optional. |
dataoptional | object | The values that fill in the blanks in your design. |
ccoptional | string | string[] | People to copy on the email. |
bccoptional | string | string[] | People to copy without others seeing them. |
replyTooptional | string | string[] | Where replies should go, if not the sender. |
attachmentsoptional | Attachment[] | Files to send along, such as a PDF receipt. |
tagsoptional | object | Labels you add to track and filter sends in the dashboard. |
unsubscribeoptional | boolean | Adds a one-click unsubscribe link to the inbox UI, as Gmail and Yahoo require for bulk mail. Needs exactly one to and no cc or bcc. Leave it off for receipts and password resets. |
Filling in data#
Most emails have parts that change for each person, like a name, an order total, or a list of items. Whatever you pass as data fills in those blanks in your design, matched by name. The subject line can use variables too.
{
"templateId": "tmpl_welcome",
"to": "timi@emiit.dev",
"subject": "Welcome, {{ name }}",
"data": {
"name": "Jane",
"plan": "Pro",
"items": [
{ "label": "Unlimited templates" },
{ "label": "Priority support" }
]
}
}If a required variable is missing or has the wrong type, the send fails with an invalid_data error that lists exactly which fields to fix, so a broken email never goes out.
Opens & clicks#
Tracking is on by default and needs no setup. Every send is logged per template, links are rewritten to count clicks, and opens are measured automatically. See it all under Analytics in your workspace.
Add tags to a send to label it, so you can tell campaigns apart in the dashboard:
await emiit.send({
templateId: "tmpl_receipt",
to: "timi@emiit.dev",
subject: "Your receipt",
tags: { campaign: "q3-launch" },
});Errors#
When an email can't be sent, the SDK throws an EmiitError with a stable code, the HTTP status, and a readable message, so you can show a friendly message or retry.
import { Emiit, EmiitError } from "@emiit/sdk";
try {
await emiit.send({
templateId: "tmpl_welcome",
to: "timi@emiit.dev",
subject: "Welcome",
});
} catch (err) {
if (err instanceof EmiitError) {
// e.g. "invalid_data" 422 "Missing or invalid variables: name (string)."
console.error(err.code, err.status, err.message);
}
throw err;
}The codes you might see:
| Code | Status | What it means |
|---|---|---|
missing_api_key | 401 | No API key was sent. Pass it when creating the client, or in the x-api-key header on the HTTP API. |
invalid_api_key | 401 | The key doesn't match any workspace. Check for a typo or a revoked key. |
template_not_found | 404 | No template with that ID exists in your workspace. |
invalid_body | 400 | The request body isn't valid JSON. |
invalid_data | 422 | Required template variables are missing or the wrong type. The fields array lists each one. |
missing_from | 400 | Thrown by the SDK before any request: no from on send() and no default on the client. |
missing_recipient | 400 | Thrown by the SDK before any request: to is empty. |
domain_not_verified | 403 | Sending with emiit from a domain your workspace hasn't verified. Add it under Settings → Domains. |
recipient_not_allowed | 403 | The shared test sender only delivers to your workspace members' own addresses. Verify a domain to reach anyone else. |
recipient_suppressed | 422 | Every to address previously bounced, reported spam, or unsubscribed. When only some are, the rest still send and the response lists the skipped ones in suppressed. |
unsubscribe_needs_single_recipient | 400 | unsubscribe was set on a send with more than one recipient. The link unsubscribes one person, so send to each separately. |
sending_paused | 403 | Sending is paused because the bounce or complaint rate got too high. The message says which. |
rate_limited | 429 | More than 10 requests in one second from the same API key. Wait a moment and retry. |
daily_limit_reached | 429 | The workspace has sent 200 emails today. Each recipient counts as one. The limit resets at 00:00 UTC. |
send_failed | 502 | Delivery rejected the message. Retry later, and check the from address if it keeps happening. |