
# Use the library

The npm package that installs the CLI is also a Node.js library. The CLI calls the same functions, so anything a command can do, your code can do.

```bash
npm install @finopsbricks/fob-email
```

It's an ES module and needs Node.js 22.13 or later.

## A client per mailbox

```js
import { fobEmail } from '@finopsbricks/fob-email';

const mbox = fobEmail('billing');
try {
  const { data } = await mbox.emails.list({ folder: 'INBOX', unseenOnly: true, limit: 20 });
  for (const envelope of data.filter((e) => e.hasAttachment)) {
    const files = await mbox.emails.download(envelope.id, { folder: 'INBOX' });
    // files: [{ filename, contentType, size, content: Buffer }]
    await mbox.emails.move(envelope.id, 'Invoices/2026', { folder: 'INBOX' });
  }
} finally {
  await mbox.close();
}
```

The client connects on first use. **Always call `close()`**, as above, or your process keeps the connection open.

## Credentials

`fobEmail()` takes an account in one of two forms:

| You pass | Where the credentials come from |
| --- | --- |
| A name, such as `'billing'`, or nothing | `FOB_EMAIL_ACCOUNTS`, then `~/.fob/fob-email/config.yml`, in the [same order as the CLI](/docs/email/cli/accounts#which-account-a-command-uses) |
| An object: `{ imap: { host, port, user, pass, tls }, smtp: { host, port, user, pass, secure } }` | Only that object. Nothing is read from the environment or config file |

In a worker, pass the object built from your own secret store. That keeps the library from depending on any file on the machine.

## What's on the client

| Namespace | Methods |
| --- | --- |
| `emails` | `list({ folder, unseenOnly, limit })`, `search({ folder, criteria, limit })`, `get(id, { folder })`, `download(id, { folder })`, `mark(id, seen, { folder })`, `move(id, to, { folder })`, `delete(id, { folder })`, `send(message)` |
| `threads` | `list({ folder, limit })`, `show(id, { folder })` |
| `drafts` | `list()`, `create(message)`, `edit(id, message)`, `delete(id)`, `send(id)` |
| `folders` | `list()`, `create(path)`, `rename(path, to)`, `delete(path)` |
| `sync` | `run({ folder, full, limit })`, `runAll({ full, limit })`, `read({ folder, unseen, limit, from, subject, since })`, `status({ folder })`, `clear({ folder })` |

- `list` and `search` return `{ data, uidValidity, folder }`, where `data` is the array of messages described in [JSON shapes](/docs/email/cli/output-and-scripting#json-shapes).
- `search` takes an [imapflow search object](https://imapflow.com/docs/guides/searching) as `criteria`, for example `{ from: 'billing@vendor.example', since: new Date('2026-09-01') }`.
- A `message` for `send` and drafts is `{ to: [...], cc, bcc, subject, text, attachments: [{ path, filename }] }`.
- `sync.read()` is synchronous and throws if the folder was never synced.

The same caveats as the CLI apply: IDs belong to a folder, and `delete` is permanent.

## Helpers

- `filterEmails(envelopes, { from, to, subject, hasAttachment, seen })` filters an array of messages in memory, like `emails filter`.
- `listEmails({ account, ...opts })` and `readEmail({ account, id, folder })` connect, do one thing and close.
- `getProfile(account)` signs in and returns `{ address, provider, threadStrategy, folders }`.
