LumboxLumbox Docs

Emails

Read emails, wait for new ones, extract OTPs.

Emails

List Emails

GET /v1/inboxes/:id/emails

Returns all emails in an inbox, newest first.

Messages forwarded from a phone appear with channel: "sms" and are receive-only. Their authentication codes are available through the same /otp and /wait?has_otp=true endpoints as email codes. See Forward SMS codes for setup and payload details.

{
  "emails": [
    {
      "id": "eml_xyz789",
      "inbox_id": "inb_abc123",
      "from": "noreply@github.com",
      "to": ["github-bot@lumbox.co"],
      "subject": "Your verification code",
      "text_body": "Your code is 847291",
      "parsed": {
        "otp_codes": ["847291"],
        "verification_links": [],
        "category": "verification"
      },
      "received_at": "2025-01-01T00:01:00.000Z"
    }
  ]
}

Wait for Email

GET /v1/inboxes/:id/wait?timeout=60

Long-polls until a matching email arrives and returns the full parsed email. Use it when you need the whole message, for example a magic link in parsed.magic_links. If nothing matches before the timeout, the response is 408.

ParamTypeDefaultDescription
timeoutnumber30Seconds to wait, 1 to 120
fromstring-Sender contains this string
subjectstring-Subject contains this string
categorystring-Parsed category, such as verification
has_otpbooleanfalseOnly return an email with an extracted code
sinceISO 8601time of the requestOnly match emails received at or after this time

Get OTP

GET /v1/inboxes/:id/otp?timeout=60

Long-polls until a verification or security email with a code arrives, then returns the code. Use it when the code is all you need.

ParamTypeDefaultDescription
timeoutnumber30Seconds to wait, 1 to 120
fromstring-Sender contains this string. A forwarded email also matches on its original sender.
sinceISO 86015 minutes before the requestOnly match emails received at or after this time
{
  "code": "847291",
  "all_codes": ["847291"],
  "from": "noreply@github.com",
  "subject": "Your verification code",
  "expires": null,
  "email_id": "eml_xyz789"
}

If no code arrives in time, the response is 408 with { "error": "No OTP found", "timeout": 60 }. Without since, the newest code from the previous five minutes is returned, and that can belong to an earlier login.

Parallel login runs

When several logins run at the same time, or one inbox serves each customer of a portal, each run needs to get its own code back:

  1. One inbox per run or per customer. A shared inbox with a sender filter cannot tell two simultaneous codes from the same service apart. Create a per-run inbox with POST /v1/inboxes and remove it with DELETE /v1/inboxes/:id when the run ends.
  2. A since timestamp captured just before the run triggers the verification email, so an older code is never returned.
  3. The expected sender in from, and an explicit 408 branch that retries the login step.
  4. An inbox-scoped key for a worker that should only reach one customer's inbox.
const API = "https://api.lumbox.co/v1";
const headers = { Authorization: `Bearer ${process.env.LUMBOX_API_KEY}` };

const since = new Date().toISOString();
await triggerLogin(inbox.address);

const res = await fetch(
  `${API}/inboxes/${inbox.id}/otp?` +
    new URLSearchParams({ since, from: "accounts.example.com", timeout: "90" }),
  { headers },
);
if (res.status === 408) throw new Error("No code arrived, retry the login");
const { code } = await res.json();

The Node SDK's waitForOtp accepts from and timeout but not since, so SDK users should give each run a fresh inbox.

SMS codes are in early access: a customer forwards login texts from their own phone with an iOS Shortcut or an Android forwarder, and the code lands in the same inbox for the same /otp call.

Get Email

GET /v1/emails/:id

Delete Email

DELETE /v1/emails/:id

Search Emails

GET /v1/emails/search?q=github&inbox_id=inb_abc123

Search across emails by keyword, sender, or category.