Omni

Developer docs

Omni API

High-speed email tunnel and extraction API for bot developers.

How it works

Your bot does not need to manage IMAP connections, passwords, or proxies. Users import their Outlook accounts into their Omni dashboard once.

Omni acts as the high-speed tunnel into those inboxes. When your software needs an OTP code, a verification link, or custom email data:

StepAction
1. Arm taskCall POST /api/otp/tasks with the email before (or as) the site sends it. Omni instantly opens a dedicated watcher on that mailbox.
2. WaitCall GET /api/otp/tasks/{id}?wait=1 to hold until the mail lands. Omni extracts the requested data (in ~200-500ms) and returns it.
3. ContinueYour bot uses the code or link and moves to the next step. For multi-step flows (e.g. OTP code first, then confirmation link), create another task for each step.

Auth

Base URL: https://omninbox.io. Get your Omni API key from Settings → Omni API (starts with omni-…).

header
Authorization: Bearer omni-…

or x-api-key: omni-…

1. Create a task

POST /api/otp/tasks: Create this before (or right as) you trigger the email.

json
{
  "email": "user@outlook.com",
  "since": 1735689600,
  "timeout": 900
}
FieldTypeRequiredDescription
emailstringyesThe Outlook address already imported in the user’s Omni dashboard.
sincenumberyesUnix timestamp in seconds (e.g. Math.floor(Date.now() / 1000)). Only mail received at or after this moment will match. This prevents stale codes from matching.
timeoutnumbernoMax seconds to wait before timing out. Default 900 (15 min), max 3600.
matchobjectnoFilters which incoming email to read (e.g. sender or subject substring). See below.
extractobjectnoSpecifies what to extract (codes, links, or custom regex). Defaults to auto-detecting OTP codes.
clientIdstringnoYour bot/worker identifier (e.g. "bot-worker-4") to query open tasks after a restart.

Response (HTTP 201):

201 created
{ "id": "5224d3d3-4966-4e74-84d9-0e16ad807932", "status": "pending" }

If the account is unhealthy (e.g. needs re-authentication or dead proxy), the response returns status: "error" immediately with an explanation so your bot can fail fast without waiting.

2. Wait for data

GET /api/otp/tasks/{id}?wait=1: Holds the HTTP connection until the matching email arrives.

200 success
{
  "id": "5224d3d3-4966-4e74-84d9-0e16ad807932",
  "status": "success",
  "code": "877668",
  "data": {
    "code": "877668"
  },
  "receivedAt": "2026-09-12T02:05:58.000Z"
}

The response fields:

FieldDescription
status"success" when matched · "timeout" if no mail arrived · "error" on failure (check error.message).
codePrimary extracted value. Contains the OTP digits, verification URL, or regex match.
dataKey-value map containing all named fields configured in your extract object.
receivedAtISO timestamp when the email landed in the mailbox.

Targeting emails & scraping data

By default, Omni automatically detects 4-8 digit verification codes, Microsoft security codes, and labeled PINs. If you need to filter specific emails or scrape links and custom text, use match and extract:

Filter (match)ExampleBehavior
match.fromIncludes"ticketmaster" or "noreply@"Only matches mail whose sender address contains this substring (case-insensitive).
match.subjectIncludes"verification" or "welcome"Only matches mail whose subject line contains this substring (case-insensitive).
Extractor (extract)SyntaxResult
Auto OTP"otp" or "code"Extracts verification codes (default when extract is omitted).
Activation link"link"Extracts the primary clickable URL from the email body.
Custom regex{ "type": "regex", "pattern": "…", "formatter": "numeric" }Applies regex to the email text (already case-insensitive — do not add (?i)). Captures group 1 (or entire match). Optional formatter: "numeric" strips non-digits — use for spaced codes like 6 9 5 6 2 7 → 695627.

Examples for bot software

1. Custom Regex

// Spaced auth codes (e.g. Ticketmaster "6 9 5 6 2 7" → 695627)
// Arm the task BEFORE the site sends mail. Pattern is already /i — do not add (?i).
const task = await fetch('https://omninbox.io/api/otp/tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'buyer@outlook.com',
    since: Math.floor(Date.now() / 1000) - 2,
    match: {
      subjectIncludes: 'Confirm Your Account',
    },
    extract: {
      code: {
        type: 'regex',
        pattern: 'Authentication Code:\\s*((?:\\d\\s*){5}\\d)',
        formatter: 'numeric', // strip spaces → "695627"
      },
    },
  }),
}).then((r) => r.json());

const done = await fetch(`https://omninbox.io/api/otp/tasks/${task.id}?wait=1`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
}).then((r) => r.json());

if (done.status === 'success') {
  console.log('code:', done.code); // 695627
}

2. Complete Universal Handler

const BASE = 'https://omninbox.io';
const API_KEY = process.env.OMNI_API_KEY;

/**
 * Universal helper for bot software.
 * Works for OTP codes, signup verification links, and custom regex extraction.
 */
async function waitForEmailData({ email, match, extract, timeout = 900 }) {
  const headers = {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  };

  // 1. Create task BEFORE triggering the email
  const createRes = await fetch(`${BASE}/api/otp/tasks`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      email,
      since: Math.floor(Date.now() / 1000) - 2, // slight skew buffer; ignore older mail
      timeout,
      ...(match ? { match } : {}),
      ...(extract ? { extract } : {}),
    }),
  });

  const task = await createRes.json();
  if (!createRes.ok || task.status === 'error') {
    throw new Error(task.error?.message || task.message || `Create failed (${createRes.status})`);
  }

  // 2. Hold connection until email arrives and data is extracted
  const waitRes = await fetch(`${BASE}/api/otp/tasks/${task.id}?wait=1`, {
    headers,
  });
  const done = await waitRes.json();

  if (done.status !== 'success') {
    throw new Error(done.error?.message || `Task ended with status: ${done.status}`);
  }

  // done.code has primary extracted string; done.data has all named fields
  return {
    code: done.code,
    data: done.data,
    receivedAt: done.receivedAt,
  };
}

3. Primary Links

// Signup flow: bot waits for activation link to finish registration
const task = await fetch('https://omninbox.io/api/otp/tasks', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'new-account@outlook.com',
    since: Math.floor(Date.now() / 1000),
    match: {
      fromIncludes: 'noreply@',
      subjectIncludes: 'confirm',
    },
    extract: {
      verifyLink: 'link', // Extracts the primary activation / magic URL
    },
  }),
}).then((r) => r.json());

// Long-poll until email lands
const done = await fetch(`https://omninbox.io/api/otp/tasks/${task.id}?wait=1`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
}).then((r) => r.json());

if (done.status === 'success') {
  const url = done.data.verifyLink;
  console.log('Activation URL:', url);
  // Bot proceeds to navigate to url
}

Errors

HTTP failures often return error as a string code plus message. When a task finishes with status: "error", error is an object:{ "code": "…", "message": "…" }. In Go, do not declare Error string — use the APIError type from the Universal Handler example (accepts both shapes).

CodeReasonFix
400account_not_foundThis Outlook address has not been imported into the user’s Omni dashboard yet. The user must add the account to Omni before the bot can read it.
400email_required / since_requiredMake sure both email and a unix timestamp since are provided.
401unauthorizedMissing or invalid Omni API key in the Authorization header.
429too_many_requests / too_many_open_tasksRate limit reached (15,000 req/min) or more than 6,000 open tasks concurrently on this key.

Concurrency & waves

Omni spins up a dedicated watcher for every unique mailbox being polled with wait=1. Multiple tasks on the same email share one efficient watcher.

When running high-volume bot fleets (hundreds or thousands of accounts), organize your runs into waves of hundreds of concurrent tasks rather than holding tens of thousands of connections indefinitely. Always use timeout: 900 (or higher) so the tail of a wave does not expire before mail arrives.

To inspect tasks without waiting: GET /api/otp/tasks/{id}
To recover pending tasks after a bot crash: GET /api/otp/tasks?clientId=my-bot&status=pending