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.