Skip to main content

Send Transactional Email From Node.js

Use this guide when a backend needs to send account emails, receipts, notifications, password resets, or onboarding messages over HTTPS.

Architecture

Use a Motor Block API Key from the Motor Block that owns the sender domain. Keep the key server-side and start with dryRun: true while integrating.

Node.js backend -> POST /v1/send -> Motor Block -> delivery + logs + webhooks

Environment Variables

MOTORICAL_API_KEY=mk_live_YOUR_KEY
MOTORICAL_FROM=sender@yourdomain.com

The from domain must match the verified domain on the Motor Block.

Minimal Send Function

export async function sendTransactionalEmail({ to, subject, text, html, idempotencyKey }) {
const response = await fetch('https://api.motorical.com/v1/send', {
method: 'POST',
headers: {
Authorization: `ApiKey ${process.env.MOTORICAL_API_KEY}`,
'Content-Type': 'application/json',
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})
},
body: JSON.stringify({
from: process.env.MOTORICAL_FROM,
to: Array.isArray(to) ? to : [to],
subject,
text,
html
})
});

const result = await response.json();
if (!response.ok || !result.success) {
throw new Error(result.error || `Motorical send failed with ${response.status}`);
}

return result.data;
}

Dry-Run First

Before sending real mail, validate the same payload with dryRun: true:

await fetch('https://api.motorical.com/v1/send', {
method: 'POST',
headers: {
Authorization: `ApiKey ${process.env.MOTORICAL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: process.env.MOTORICAL_FROM,
to: ['recipient@example.com'],
subject: 'Dry run',
text: 'Validate this before sending.',
dryRun: true
})
});

Production Notes

  • Add Idempotency-Key for emails triggered by retryable jobs, payments, signups, or webhooks.
  • Use separate Motor Blocks for production and staging.
  • Use delivery logs and webhooks to confirm whether queued messages were delivered, bounced, or failed.
  • Do not put mk_live_... keys in browser code.