Developer Guide
From your first transactional email to drip sequences — everything you need to know about sending email programmatically in 2026.
Sending an email through an API sounds simple — and it is, for one email. But building a reliable email system that handles transactional confirmations, drip sequences, and high volume without ending up in spam? That requires understanding a few things first.
This guide covers the full picture: picking an email API, authenticating your domain, sending your first email, and scaling to automation sequences — with real code examples throughout.
The email API market splits into two categories: infrastructure providers and full-platform tools.
Handle SMTP relay, deliverability, and basic analytics. You own the logic.
API + sequences + contacts + analytics. Bring your own sending key (BYOK) to avoid markup.
If you just need to fire off password resets, start with a pure infrastructure provider. If you need sequences, contact management, or automation — pick a platform tool that lets you BYOK so you're not paying double for sends.
Before you send a single email, authenticate your domain. Without it, you'll hit spam folders — sometimes immediately. All major mailbox providers (Gmail, Outlook) check these records.
SPF (Sender Policy Framework) tells receiving servers which IP addresses are authorized to send email from your domain. Add a TXT record to your DNS:
DKIM (DomainKeys Identified Mail) signs your emails with a cryptographic key, letting receivers verify the message hasn't been tampered with. Your email platform generates a DKIM keypair; you add the public key as a TXT record:
DMARC ties SPF and DKIM together and tells receivers what to do with mail that fails either check. Start with p=none to monitor, then harden to p=quarantine or p=reject once you're confident:
Every email API follows the same pattern: authenticate with an API key, POST a JSON payload with from/to/subject/body, handle the response. Here's what that looks like with a few providers:
curl -X POST https://api.tinysend.co/v1/emails \
-H "Authorization: Bearer ts_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome to the app!",
"html": "<h1>Welcome</h1><p>Thanks for signing up.</p>"
}'import { Tinysend } from '@tinysend/node'
const client = new Tinysend({ apiKey: process.env.TINYSEND_API_KEY })
await client.emails.send({
from: '[email protected]',
to: req.user.email,
subject: 'Confirm your email address',
template: 'confirm-email',
variables: {
firstName: req.user.name,
confirmUrl: `https://app.example.com/confirm?token=${token}`
}
})import requests
response = requests.post(
"https://api.tinysend.co/v1/emails",
headers={
"Authorization": f"Bearer {os.environ['TINYSEND_API_KEY']}",
"Content-Type": "application/json"
},
json={
"from": "[email protected]",
"to": user.email,
"subject": "Password reset",
"template": "password-reset",
"variables": {"resetUrl": reset_url}
}
)
response.raise_for_status()This distinction matters for deliverability, compliance, and infrastructure setup.
| Type | Examples | Unsubscribe required? | Send separately? |
|---|---|---|---|
| Transactional | Password reset, receipts, order confirmation, alerts | No | Yes — dedicated IP/domain |
| Marketing | Newsletters, promotions, announcements | Yes (CAN-SPAM/GDPR) | Yes — separate domain/IP |
| Product (drip) | Onboarding sequences, re-engagement, feature tips | Best practice: yes | Ideally separate |
Keep transactional email on a separate subdomain (e.g. mail.yourdomain.com) from marketing. If your marketing mail gets flagged for spam, it won't nuke your transactional reputation.
Set up webhooks to track what happens after you send. At minimum, handle these events:
// Express webhook handler
app.post('/webhooks/email', express.raw({ type: 'application/json' }), (req, res) => {
const event = JSON.parse(req.body)
switch (event.type) {
case 'email.bounced':
await db.contacts.update(
{ email: event.data.to },
{ $set: { status: 'bounced', bouncedAt: new Date() } }
)
break
case 'email.complained':
await db.contacts.update(
{ email: event.data.to },
{ $set: { unsubscribed: true, unsubscribedAt: new Date() } }
)
break
case 'email.opened':
await sequences.advance(event.data.contactId, event.data.sequenceId)
break
}
res.json({ received: true })
})Once you go beyond one-off transactional emails, you need sequences: scheduled series of emails triggered by user behavior. A typical SaaS onboarding sequence looks like this:
With tinysend, you define sequences as code in a YAML or JSON config and deploy via CLI. No dragging blocks in a UI:
# tinysend sequence definition
name: onboarding
trigger:
event: user.signed_up
steps:
- delay: 0
template: welcome
condition: null
- delay: 2d
template: feature-highlight
condition: "contact.properties.has_completed_setup == false"
- delay: 7d
template: check-in
condition: "contact.properties.has_completed_setup == false"
- delay: 14d
template: upgrade-nudge
condition: "contact.properties.plan == 'free'"Even with perfect domain auth, you can still land in spam. Here's what actually moves the needle:
no-reply@ addresses as lower-reputation. Use team@ or hello@ for better inbox placement.tinysend gives you a full email platform — transactional sends, sequences, contact management, and BYOK. API-first, usage-based, free to start.
Get started free →