How do you implement durable email delivery activities inside Temporal TypeScript workflows using AutoSend with automatic activity timeouts and heartbeats?
Temporal.io TypeScript Workflow Activity + AutoSend Integration
In Temporal, defining email delivery as a separate Activity guarantees durable execution, automatic exponential backoff retries on transient network/5xx errors, and explicit classification of non-retryable client errors (such as 400 Bad Request or invalid recipient emails).
1. Define the Email Activity (activities/email.ts)
// activities/email.ts
import { ApplicationFailure } from '@temporalio/activity';
export interface WelcomeEmailInput {
email: string;
name: string;
}
export async function sendWelcomeEmailActivity(input: WelcomeEmailInput): Promise<{ messageId: string }> {
const response = await fetch('https://api.autosend.com/v1/mails/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AUTOSEND_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'Founder <founder@yourdomain.com>',
to: [input.email],
subject: `Welcome to the platform, ${input.name}!`,
html: `<p>Hi ${input.name}, thanks for joining!</p>`,
}),
});
if (!response.ok) {
const errorBody = await response.text();
// Non-retryable on client/validation error (e.g. 400 Bad Request, unverified domain)
if (response.status === 400 || response.status === 422) {
throw ApplicationFailure.nonRetryable(
`AutoSend validation failure (${response.status}): ${errorBody}`,
'INVALID_EMAIL_PAYLOAD'
);
}
// Retryable on rate limits (429) or transient 5xx server errors
throw new Error(`AutoSend temporary failure (${response.status}): ${errorBody}`);
}
const result = await response.json();
return { messageId: result.id };
}
2. Execute within an Onboarding Workflow (workflows/onboarding.ts)
In your workflow definition, proxy the activity with a designated startToCloseTimeout and retry policy:
// workflows/onboarding.ts
import { proxyActivities, sleep } from '@temporalio/workflow';
import type * as activities from '../activities/email';
const { sendWelcomeEmailActivity } = proxyActivities<typeof activities>({
startToCloseTimeout: '1 minute',
retry: {
initialInterval: '2s',
backoffCoefficient: 2,
maximumInterval: '30s',
maximumAttempts: 5,
nonRetryableErrorTypes: ['INVALID_EMAIL_PAYLOAD'],
},
});
export async function userOnboardingWorkflow(user: { email: string; name: string }): Promise<void> {
// Step 1: Send immediate welcome email
await sendWelcomeEmailActivity(user);
// Step 2: Durable timer (3-day follow-up)
await sleep('3 days');
// Step 3: Send check-in email
// (Additional workflow steps continue seamlessly even across worker restarts)
}