Sending an email is usually the easy part. Understanding what happened after the send is where developers start needing better tools.
Did the message queue? Did it send? Was it delivered? Did the recipient open it? Did they click the link? Did it bounce? If you are building product notifications, onboarding flows, password resets, receipts, billing alerts, or lifecycle messaging, those answers matter as much as the send request itself.
That is the idea behind the setup-email-api-nodejs sample. It is a small local dashboard that shows the Telnyx Email API loop end to end:
- Read a Telnyx API key and sender/recipient addresses from
.env. - Send one email with
POST /v2/email_messages. - Enable open and click tracking on that send.
- Poll message events.
- Display delivery, open, click, bounce, and unsubscribe rates in a local browser UI.
The goal is not to build a production analytics platform in one file. The goal is to make the Email API feel concrete: one request sends the message, and the event feed tells you what happened next.
What the Sample Builds
The sample is intentionally plain Node.js. There are no npm dependencies and no framework layer to understand before you get to the API calls. The app has one server file and one HTML page:
server.jsloads.env, sends the email, polls Telnyx events, masks configured email addresses, and exposes local JSON endpoints.index.htmlrenders the dashboard, calls the local API, and refreshes stats every 10 seconds.
The only environment variables required are:
TELNYX_API_KEY=KEY_your_telnyx_api_key_here
FROM_EMAIL=sender@example.com
TO_EMAIL=you@example.com
FROM_EMAIL must be a sender that your Telnyx account can use. The app keeps the API key on the server and never returns it to the browser.
The Send Request
The core request is POST /v2/email_messages. In the sample, it sends a basic HTML and text email:
const r = await telnyx("POST", "/email_messages", {
from: {
email: FROM_EMAIL,
name: "Telnyx Email API Demo",
},
to: [{ email: TO_EMAIL }],
subject: `Telnyx Email API Demo - ${now}`,
html_body: `<h2>Telnyx Email API Demo</h2>
<p>Sent at ${now} from the local dashboard.</p>
<p><a href="https://telnyx.com/products/email-api">Click this link</a> to generate a click event.</p>`,
text_body: `Telnyx Email API Demo sent at ${now}. Link: https://telnyx.com/products/email-api`,
tracking_settings: {
open_tracking: true,
click_tracking: true,
},
tags: ["dashboard-test"],
});
The important part for engagement metrics is tracking_settings.
Delivery lifecycle events come from the normal Email API event feed. Open and click events require tracking. In this sample, tracking is enabled per send:
tracking_settings: {
open_tracking: true,
click_tracking: true,
}
If tracking_settings is omitted, the message inherits the sender domain's default tracking settings. That distinction is useful when debugging: domain-level tracking settings are defaults, while message-level settings can be used to make a specific send emit open and click events.
Polling the Event Feed
After the send request returns, the app stores the message ID locally and polls Telnyx for events:
GET /v2/email_messages/{id}/events
The app also includes a fallback to:
GET /v2/email_events?email_id={id}
That gives the dashboard the event timeline it needs to calculate rates. A single message can move through events like:
email.queuedemail.sendingemail.sentemail.deliveredemail.openedemail.clickedemail.bouncedemail.unsubscribed
For the UI, the sample normalizes those event names by removing the email. prefix and then counts unique messages by event type.
Turning Events Into Dashboard Metrics
The dashboard calculates five rates:
- Delivery rate - delivered messages divided by sent messages
- Open rate - opened messages divided by delivered messages
- Click rate - clicked messages divided by delivered messages
- Bounce rate - bounced messages divided by sent messages
- Unsubscribe rate - unsubscribed messages divided by delivered messages
For a one-message local demo, those numbers are simple. Send one message, wait for delivery, open the email, click the link, and the dashboard should move from delivery-only to open and click metrics as events appear.
The real value is the pattern: for a larger product workflow, the same event stream can feed internal dashboards, alerting, customer-support context, or lifecycle analytics.
Safe Defaults for a Demo
The sample includes a few guardrails that are easy to miss in quick demos:
- The API key is only used server-side.
- Configured sender and recipient addresses are masked in the browser.
- Local message state is stored in
data/sent.json, which is ignored by git. .envis ignored by git, and.env.examplecontains only placeholders.
That keeps the example safe to screen share while still showing real Email API behavior.
Where This Fits
This is the first working loop: send an email and observe what happens. It is a useful starting point before adding the rest of the Email API surface area:
- custom domain creation and DNS verification
- templates
- scheduled sends
- suppression handling
- webhooks
- inbound inboxes and replies
- persistent storage for long-running analytics
The sample keeps those out on purpose. If you are learning the API or recording a demo, fewer moving parts make the lifecycle easier to see.
Try It
Clone the code sample:
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/setup-email-api-nodejs
cp .env.example .env
npm start
Then open:
http://localhost:3000
Add your Telnyx API key, sender, and recipient to .env, send the test email, open it, click the link, and watch the event log update.
If you want the shortest possible mental model, it is this: the send request starts the workflow, and the event feed tells the truth about what happened next.