Inngest took the parts of writing a real application that always suck — queues, retries, cron, long-running jobs — and collapsed them into a single primitive: the event-driven function. It's the right partner for Lumbox, because inbound email is the event layer most agent backends are missing.

The wiring

Lumbox fires a webhook when mail arrives. Point that webhook at an Inngest event endpoint. Inngest's function handles the parsed email with full retry + observability.

// Point Lumbox webhook at: https://inn.gs/e/YOUR_INGEST_KEY
// Events emitted as "lumbox.email.received"

import { inngest } from "./client";

export const handleInboundEmail = inngest.createFunction(
  { id: "handle-inbound-email" },
  { event: "lumbox.email.received" },
  async ({ event, step }) => {
    const { from, subject, text, extracted } = event.data;

    if (extracted.otp) {
      await step.run("resolve-pending-signup", async () => {
        return resolvePendingSignup(from, extracted.otp);
      });
    }

    await step.run("log-to-crm", () => logEmail(from, subject, text));
  },
);

Scheduled follow-ups

Use Inngest's step.sleep to schedule a follow-up email in a day, a week, a month. Lumbox handles the send, Inngest handles the durability.

export const followUp = inngest.createFunction(
  { id: "follow-up" },
  { event: "signup.completed" },
  async ({ event, step }) => {
    await step.sleep("wait-3-days", "3d");
    await step.run("send-followup", () =>
      lumbox.inboxes.send(AGENT_INBOX, {
        to: event.data.email,
        subject: "How's it going?",
        text: "Checking in from your onboarding agent.",
      }),
    );
  },
);

lumbox.co.