imapflow

Modern Node.js IMAP client library (imapflow) for email integration.Covers authentication, mailbox locking, streaming fetches, async iterators, reconnection strategies, proxy support, and provider-specific configs (Gmail, Outlook, Yahoo, etc.).

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "imapflow" with this command: npx skills add openlark/imapflow

ImapFlow

Overview

ImapFlow is a modern, promise-based IMAP client for Node.js. It auto-detects and handles IMAP extensions (CONDSTORE, QRESYNC, IDLE, COMPRESS, etc.) so the same code works across servers.

Key features: async/await API, async iterators for message streaming, built-in mailbox locking, TypeScript support, SOCKS/HTTP proxy support, Gmail X-GM-EXT-1 support.

Trigger Scene

Use when building email features: connecting to IMAP servers, fetching/reading emails, searching mailboxes, managing folders, monitoring new messages via IDLE, working with Gmail labels and Gmail-specific extensions, or any IMAP-based email automation.

Quick Start

const { ImapFlow } = require('imapflow');

const client = new ImapFlow({
    host: 'imap.example.com',
    port: 993,
    secure: true,
    auth: { user: 'user@example.com', pass: 'password' }
});

await client.connect();

let lock = await client.getMailboxLock('INBOX');
try {
    // Fetch latest message
    let msg = await client.fetchOne(client.mailbox.exists, { source: true });
    console.log(msg.source.toString());

    // Stream all messages
    for await (let msg of client.fetch('1:*', { envelope: true })) {
        console.log(`${msg.uid}: ${msg.envelope.subject}`);
    }
} finally {
    lock.release();
}

await client.logout();

Core Tasks

1. Connecting

const client = new ImapFlow({
    host: 'imap.example.com',
    port: 993,
    secure: true,
    auth: { user: 'user@example.com', pass: 'password' }
});
await client.connect();

Provider-specific configs → See references/connection.md for Gmail, Outlook, Yahoo, iCloud, and other common providers.

OAuth2 / XOAUTH2:

auth: {
    user: 'user@gmail.com',
    accessToken: 'ya29.xxx...'
}

Common options:

  • logger: Pass a logger object (console, pino, etc.) for debug output. Set logger: false to disable.
  • emitLogs: Set true to emit 'log' events instead of using a logger.
  • clientInfo: Custom client identification { name: 'myapp', version: '1.0.0' }.
  • disableAutoIdle: Disable automatic IDLE when mailbox is selected.

Disconnect: Call client.logout() for graceful disconnect. Handle client.close() for abrupt close.

2. Fetching Messages

Always acquire a mailbox lock before fetching:

let lock = await client.getMailboxLock('INBOX');
try {
    // ... fetch operations ...
} finally {
    lock.release();
}

Fetch one message:

let msg = await client.fetchOne('*', { source: true });
// or by sequence number: client.fetchOne(42, { source: true })
// or by UID (append `uid` flag):
let msg = await client.fetchOne('12345', { uid: true, source: true });

Fetch query options (what data to retrieve — choose only what you need):

  • source — Full RFC822 message source (as Buffer)
  • envelope — Parsed envelope (subject, from, to, date, message-id)
  • bodyStructure — MIME structure tree
  • flags — Array of flags (\Seen, \Answered, etc.)
  • internalDate — Internal server date
  • size — Message size in bytes
  • uid — UID (always included)
  • threadId — THREAD=ORDEREDSUBJECT reference (Gmail)
  • labels — Gmail labels (X-GM-LABELS)
  • headers — Raw headers as Buffer
  • bodyParts — Array of MIME part paths to fetch, e.g. ['1.1', '1.2']

Stream/iterate messages:

// Range: sequence numbers
for await (let msg of client.fetch('1:100', { envelope: true, flags: true })) {
    console.log(`${msg.seq}: ${msg.envelope.subject}`);
}

// All messages: '1:*'
for await (let msg of client.fetch('1:*', { envelope: true })) { /* ... */ }

// By UID range
for await (let msg of client.fetch('1000:2000', { uid: true, envelope: true })) { /* ... */ }

Fetch specific body parts:

let msg = await client.fetchOne('*', {
    bodyParts: ['1.1', '1.2'],  // MIME part paths
    source: true
});
// msg.bodyParts.get('1.1') → Buffer
// msg.text.toString() → decoded text content

Download attachments:

let msg = await client.fetchOne('*', { source: true, bodyStructure: true });
for (let attachment of msg.attachments) {
    let buf = await client.download(msg.uid, attachment.part, { uid: true });
    // buf contains the attachment data
}

Common envelope fields: msg.envelope.subject, msg.envelope.from[0].address, msg.envelope.to[0].address, msg.envelope.date, msg.envelope.messageId, msg.envelope.inReplyTo.

3. Searching

let lock = await client.getMailboxLock('INBOX');
try {
    // Simple search
    let list = await client.search({ unseen: true });

    // Complex query
    let list = await client.search({
        from: 'sender@example.com',
        subject: 'invoice',
        seen: false,
        since: new Date('2024-01-01'),
        before: new Date('2024-12-31'),
        larger: 1024 * 1024  // > 1MB
    });

    // Text search (body)
    let list = await client.search({ body: 'important keyword' });

    // Combine with OR
    let list = await client.search({
        or: [
            { from: 'alice@example.com' },
            { from: 'bob@example.com' }
        ]
    });

    // Combine AND + OR
    let list = await client.search({
        subject: 'report',
        or: [
            { from: 'alice@example.com' },
            { from: 'bob@example.com' }
        ]
    });

    // List is an array of sequence numbers. Use fetch() with those:
    for await (let msg of client.fetch(list, { envelope: true })) {
        console.log(msg.envelope.subject);
    }
} finally {
    lock.release();
}

Search keys → See references/searching.md for the full IMAP search key reference.

Gmail raw search (X-GM-RAW):

let list = await client.search({ 'x-gm-raw': 'has:attachment larger:10M' });

4. Mailbox Management

// List all mailboxes
let mailboxes = await client.list();
for (let mb of mailboxes) {
    console.log(`${mb.name} (${mb.path})`);
}
// Filter by pattern: client.list({ path: 'INBOX/*' })

// Status (message count, unseen, etc.)
let status = await client.status('INBOX', { messages: true, unseen: true, uidNext: true });
console.log(`${status.messages} total, ${status.unseen} unseen`);

// Mailbox info (when a mailbox is selected)
console.log(client.mailbox.exists);    // total messages
console.log(client.mailbox.uidNext);   // next predicted UID (CONDSTORE)
console.log(client.mailbox.uidValidity);

// Create / Rename / Delete
await client.mailboxCreate('Projects/NewProject');
await client.mailboxRename('Projects/Old', 'Projects/New');
await client.mailboxDelete('Projects/Archive');

// Subscribe / Unsubscribe
await client.mailboxSubscribe('INBOX/Newsletters');
await client.mailboxUnsubscribe('INBOX/Newsletters');

// Move messages
await client.messageMove('1:5', 'Archive', { uid: false });
// Copy messages
await client.messageCopy('100:200', 'Important', { uid: true });

// Delete messages (set \Deleted flag + expunge)
await client.messageDelete('1:10');
// Or manually: flag + expunge
await client.messageFlagsAdd('1:10', ['\\Deleted']);
await client.messageExpunge('1:10');  // or expunge all: client.mailboxExpunge()

5. IDLE (Push Notifications)

ImapFlow automatically enters IDLE when there's an active mailbox lock and no pending commands. Use events to react to new messages:

client.on('exists', async (data) => {
    console.log(`New messages! Count: ${data.count}`);
    // Fetch new messages
    let lock = await client.getMailboxLock('INBOX');
    try {
        for await (let msg of client.fetch(`${data.prevCount + 1}:${data.count}`, { envelope: true })) {
            console.log(`New: ${msg.envelope.subject}`);
        }
    } finally {
        lock.release();
    }
});

client.on('expunge', (data) => {
    console.log(`Message seq#${data.seq} was deleted`);
});

client.on('flags', (data) => {
    console.log(`Flags changed for seq#${data.seq}: ${data.flags}`);
});

// Connect and select mailbox — IDLE starts automatically
await client.connect();
let lock = await client.getMailboxLock('INBOX');
// Keep lock active — IDLE runs in background

Manual IDLE control:

client.on('idle', () => console.log('Entered IDLE'));
// To disable auto-IDLE: new ImapFlow({ disableAutoIdle: true })

6. Gmail-Specific Operations

// Gmail labels
let msg = await client.fetchOne('*', { labels: true });
console.log(msg.labels); // ['\\Inbox', 'Important', 'Starred']

// Set labels
await client.messageFlagsAdd('1', ['\\Starred', 'Important'], { uid: true });
await client.messageFlagsRemove('100', ['Important'], { uid: true });
await client.messageFlagsSet('42', ['\\Inbox', 'CustomLabel'], { uid: true });

// Gmail raw search
let results = await client.search({ 'x-gm-raw': 'from:alice has:attachment newer_than:7d' });

// Gmail thread ID
let msg = await client.fetchOne('*', { threadId: true });
console.log(msg.threadId); // Gmail thread identifier

Gmail connection config:

new ImapFlow({
    host: 'imap.gmail.com',
    port: 993,
    secure: true,
    auth: {
        user: 'user@gmail.com',
        accessToken: 'oauth2-token'  // Use OAuth2
    }
})

Note: Gmail requires OAuth2 or App Passwords. Regular passwords won't work unless "Less secure app access" is enabled (deprecated).

7. Error Handling & Reconnection

Connection events:

client.on('error', (err) => {
    console.error('IMAP error:', err.message);
    // Connection is likely closed after a fatal error
});

client.on('close', () => {
    console.log('Connection closed');
    // Reconnect logic here
});

client.on('connectionError', (err) => {
    console.error('Connection failed:', err.message);
});

Reconnection pattern:

async function connectWithRetry(config, maxRetries = 5) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            const client = new ImapFlow(config);
            await client.connect();
            return client;
        } catch (err) {
            console.error(`Connection attempt ${i + 1} failed: ${err.message}`);
            if (i < maxRetries - 1) {
                await new Promise(r => setTimeout(r, Math.min(1000 * Math.pow(2, i), 30000)));
            }
        }
    }
    throw new Error('All connection attempts failed');
}

Mailbox locking best practices:

  • Always use try/finally to release locks
  • Keep lock durations short — release between operations when possible
  • Never hold a lock across network calls or long async operations
  • Use separate locks for read vs write operations when safe
// BAD: lock held too long
let lock = await client.getMailboxLock('INBOX');
let results = await client.search({ unseen: true });
// ... process results (slow, lock held) ...
lock.release();

// GOOD: release lock between operations
let results;
{
    let lock = await client.getMailboxLock('INBOX');
    results = await client.search({ unseen: true });
    lock.release();
}
// Process results without holding lock
for (let seq of results) { /* ... */ }

Timeouts and connection health:

// No operation timeout (blocks forever) — set one
const timeout = setTimeout(() => client.close(), 30000);
// ... your operation ...
clearTimeout(timeout);

// Check if connected: client.usable → true/false

Reference Files

  • connection.md — Provider-specific connection configs: Gmail, Outlook/Hotmail, Yahoo, iCloud, Zoho, Fastmail, custom servers. Includes TLS, OAuth2, App Passwords, and proxy setup.
  • searching.md — Complete IMAP search key reference: flags, dates, sizes, headers, text searches, logical operators, sequence sets, Gmail raw search.
  • api_reference.md — Key API methods summary: client methods, events, MailboxLock, FetchQueryObject options, and ImapFlow constructor options.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

React Native Update (Pushy) Integration

Unified integration skill for React Native Update / Pushy(统一入口)across OpenClaw and Claude Code workflows. Use for 安装配置, appKey/update.json 接线, iOS/Android 原生...

Registry SourceRecently Updated
Coding

Gateway Guard

Ensures OpenClaw gateway auth consistency. Use when checking or fixing gateway token/password mismatch, device_token_mismatch errors, or before delegating to...

Registry SourceRecently Updated
Coding

Gog Html Email

Send beautifully formatted HTML emails via gog CLI with templates and styling

Registry SourceRecently Updated
Coding

Q Kdb Code Review

AI-powered code review for Q/kdb+ — catch bugs in the most terse language in finance

Registry SourceRecently Updated