Bichon Docs

Supported Formats

FormatToolNotes
EML Directorybichon-cli / WebUICLI: recursive directory scan preserving hierarchy; WebUI: individual .eml files (multi-select)
MBOXbichon-cli / WebUISingle-file streaming import; supports Gmail's MBOX variant
Thunderbirdbichon-cliReads directly from local Thunderbird profile directory
PSTbichon-cli / WebUIOutlook Personal Storage (.pst) file parsing; folder structure preserved
API ImportPOST /api/v1/importBase64-encoded EML payloads for programmatic use
MBOX Exportbichon-cli / WebUIWhole messages as .mbox — an entire account (CLI), a saved search, or the current result set (WebUI)
Attachment Exportbichon-cli / WebUIMatching attachments packed into a .zip, deduplicated by content hash, with a manifest.jsonl
Export VerificationPOST /api/v1/exports/{job_id}/verifyRecompute artifact hash and cross-check every message or attachment (Pro)

WebUI limits: MBOX uploads up to 1 GB per file, PST up to 2 GB (both configurable server-side via bichon_web_mbox_upload_limit_mb / bichon_web_pst_upload_limit_mb). EML files are capped at 100 MB. For larger archives use bichon-cli, which processes server-side with deduplication.

Importing via the WebUI

The Import page (Import EML / MBOX / PST) uploads files directly through the browser to POST /api/v1/upload-import. It walks through four steps:

1. Select target account

Only NoSync accounts are listed. Accounts that are actively syncing via IMAP are excluded — importing into a live sync pipeline would race the fetcher, so such accounts must use the CLI path or be converted. Your account needs the data:import:batch permission on the target account (administrators with system:root bypass account ACLs); otherwise the import will be rejected by the server. See Accounts for the account types.

2. Choose files

Drag & drop or browse for one or more .eml, .mbox, or .pst files. Files that exceed the size limits or have a non-email MIME type (videos, archives, PDFs, executables, …) are rejected upfront in the queue list. The server additionally checks free disk space before streaming and aborts if less than 2× the upload limit is available.

3. Folder determination

PST files carry their own folder structure (Inbox, Sent Items, …) which is preserved and created automatically during import — no folder choice is offered.

EML / MBOX files offer three modes for deciding which mail folder the messages land in:

ModeBehavior
Auto-detect from email headers (default)Reads the first 64 KB of the file in-browser and picks the folder by priority: ① X-Bichon-Metadata header (a JSON object with a mailbox_name field — written by Bichon's own exports, so re-imports round-trip), ② X-Gmail-Labels header (Gmail Takeout; state labels like Unread/Opened/Archived — including localized variants in 20+ languages — are filtered out, and business labels are preferred over generic Inbox/Sent), ③ file name fallback (Inbox.mbox → Inbox). The detected folder and its source are shown as a badge before you commit.
Choose from existing mailboxesPick one of the mailboxes already present in the selected account.
Enter a custom folder nameType any target folder name; it is created on import.

4. Progress & results

The upload streams with a progress bar, then the server parses, indexes, and deduplicates each message. Live counters show success / failed / duplicates (duplicates are content-identical messages already in the archive, detected by BLAKE3 hash — they are skipped, not re-imported). Failed items are listed with per-message error messages, expandable inline.

An import history section at the bottom keeps your past runs with the same success/failed/duplicate/total breakdown per run.

Exporting

Bichon exports archived mail two ways. Both run server-side as background jobs — the browser or CLI only starts the job, polls it, and downloads the finished artifact; nothing is assembled client-side.

Export typeFormatContentsSource
Whole messages.mboxOne MBOX file with every matching messageEmail saved search, or an email-result filter
Attachments.zipEvery matching attachment, deduplicated by content hash, plus a manifest.jsonl describing each entryAttachment saved search, or an attachment-result filter

MBOX requires an Email saved search and the attachment zip requires an Attachment saved search; the server rejects a mismatched pair rather than silently exporting something else. The same rule applies to ad-hoc filters: an email filter only pairs with MBOX, an attachment filter only with the zip.

Entry points

  • Exports page (/exports) — pick a saved search. The export replays that search's stored filter, so the scope is reproducible and can be run again later.
  • “Download all” on the search / attachments page — exports the current filter without saving a search first. The button only appears when the page narrows the result set with at least one criterion (text query, account, mailbox, tag, metadata, or date range); with no filter there would be nothing to export but the whole archive, so the button is hidden. This export is labelled Current filter in the job list and on the approval card.

Preview and job lifecycle

  1. Preview — the server returns the accounts, item count, and estimated size the selection covers (POST /exports/preview). Nothing has been exported yet.
  2. Start — creates a background job (POST /exports). The page shows processed / exported / failed counters and supports cancel.
  3. Download — once finished, download the artifact. A failed item (for example, an attachment whose blob is missing from storage) is counted in failed and does not abort the rest of the job.

Where artifacts live and how long they stay

<root>/exports/<job_id>/
├── exp_xxx.mbox        # or exp_xxx.zip
├── manifest.json       # job summary + SHA-256 of the artifact
└── manifest.jsonl      # attachment exports only: one line per zip entry
  • Finished artifacts are kept for 1 hour (EXPORT_TTL_SECS). A sweep runs every 15 minutes and also removes orphaned job directories, so download before the hour is up. Deleting a job removes its directory immediately.
  • Completed jobs survive a server restart — they are reloaded from manifest.json. Jobs that were still running do not: the transfer cannot be resumed, and the partial directory is reclaimed by the TTL sweep.
  • exports/ is runtime scratch space: it is not included in backups (the backup engine captures the databases, blob store, and indexes — not runtime scratch directories) and it cannot be relocated separately from the data root.

Operational limits

  • Two concurrent exports. A third request is rejected until one finishes.
  • Disk space. Before starting, the server checks that the export volume has the estimated size × 1.2 free and fails fast if not.
  • Scope. An export is limited to accounts the caller may read and requires data:export:batch on every account in scope. A view whose access is narrowed by an address filter cannot be used to scope a batch export — the server refuses rather than widening it to the whole source account.
  • Dual control. On Enterprise, export.batch can be placed under dual control. Starting an export then creates a pending approval instead of a job (the API answers 202), and the export runs only after a second person approves. The approval records the exact selection — saved search id, or the ad-hoc filter frozen at request time — so what runs is what was reviewed.

Download tickets

GET /api/v1/exports/{job_id}/download requires the Bearer token. For browser downloads use POST /api/v1/exports/{job_id}/download-ticket: it returns a URL that is single-use and valid for 60 seconds, so large artifacts stream without putting the token into a navigation URL.

Verification (Pro)

Pro can recompute the artifact's SHA-256 and cross-check every entry against the live archive — messages for an MBOX export, attachment content hashes for a zip export. Results are persisted and survive a restart.

API

POST /exports/preview and POST /exports take the same body. Provide exactly one of:

  • saved_search_id — replay a saved search; or
  • filter — an ad-hoc selection, shaped {"email": {…}} for MBOX or {"attachment": {…}} for a zip. Add label to name the export in the job list.

format is "Mbox" (default) or "Attachments".

MethodPathDescription
POST/api/v1/exports/previewPreview the selection's scope (accounts, count, size)
POST/api/v1/exportsStart an export job (202 if dual control queues it)
GET/api/v1/exportsList your export jobs
GET/api/v1/exports/{job_id}Job status and counters
POST/api/v1/exports/{job_id}/cancelCancel a running job
DELETE/api/v1/exports/{job_id}Delete a job and its artifact
GET/api/v1/exports/{job_id}/downloadDownload the artifact (MBOX or ZIP)
POST/api/v1/exports/{job_id}/download-ticketOne-time, 60-second download URL
POST/api/v1/exports/{job_id}/verifyVerify the artifact and every entry (Pro)

Processing Pipeline

All imports flow through the Bichon REST API. The server:

  1. Parses MIME structure
  2. Extracts metadata
  3. Indexes content into Tantivy
  4. Deduplicates by BLAKE3 content hash
  5. Stores raw blobs in bichon-blob

If the target account has an archive filter, messages the filter rejects are not stored. They still count as successful imports — the file was processed; there is simply no message in the archive.

On this page