Temporal solves the problem every async workflow eventually hits: what happens if the process dies mid-flow? For email, it's acute — you kick off a signup, the worker crashes, and now you have a Lumbox inbox waiting for an OTP that no one is listening for. Temporal activities make that durable.
The pattern: inbox creation as an activity
import { proxyActivities } from "@temporalio/workflow";
import type * as activities from "./activities";
const { createInbox, submitSignup, waitForOtp, submitOtp } =
proxyActivities<typeof activities>({
startToCloseTimeout: "5 minutes",
});
export async function signupWorkflow(service: string, user: User) {
const inbox = await createInbox(`signup-${service}`);
await submitSignup(service, user, inbox.address);
const otp = await waitForOtp(inbox.id, 120);
await submitOtp(service, otp);
return { success: true, address: inbox.address };
}
Long waits are fine
Some verification flows send a link that expires in 24 hours. Temporal can hold a workflow open for days with no resource cost — when the worker comes back, the state picks up. Lumbox inboxes don't expire unless you tell them to, so a workflow that sleeps for a day and then checks the inbox works exactly as expected.
Retries are automatic
If waitForOtp times out, Temporal retries per your activity policy. Lumbox's timeout is server-side — the HTTP call cleanly returns after the timeout window, and Temporal handles the rest. lumbox.co.