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-Keyfor 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.