Mailbox History Backfill Guide
This guide covers the mailbox history backfill API — a one-time import of a connected mailbox's historical emails, delivered to your webhook.
Backfill is currently supported only for Google (Gmail) OAuth IMAP mailboxes. Outlook/Microsoft and generic IMAP are not supported yet. The mailer you target must be a connected Gmail OAuth IMAP account with a configured webhook.
Overview
When a mailbox is connected, the live poller only forwards mail that arrives after connection. Backfill imports the historical emails that predate (or surround) that point, so a newly connected mailbox can build a complete communication timeline.
How it works:
- You start a backfill for a connected mailer. The platform walks the mailbox newest-first and delivers each email individually to the mailer's configured webhook (the same
IMAP_OAUTHincoming-email webhook used by live mail). - Delivery is at-least-once: a slice may be retried (on transient errors, redelivery, or resume), so the same email can be delivered more than once. Your webhook must dedupe by
Message-Id. - The job runs asynchronously; poll the status endpoint for progress, or abort it at any time.
Prerequisites
- A connected Gmail OAuth IMAP mailer (
mailerID). - A webhook configured on that mailer (backfilled emails are delivered there).
- A valid API token (the same auth used for other
/v1endpoints).
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/{mailerID}/backfill | Start a backfill job |
GET | /v1/{mailerID}/backfill/{jobId} | Get job status / progress |
POST | /v1/{mailerID}/backfill/{jobId}/abort | Cancel an in-flight job |
See the API Reference → Backfill section for the full generated schema.
Start a backfill
POST /v1/{mailerID}/backfill
Provide at least one of window_start or count:
| Field | Type | Required | Notes |
|---|---|---|---|
window_start | string (RFC3339) | required unless count is given | Oldest point to walk back to, e.g. 2025-01-01T00:00:00Z. |
window_end | string (RFC3339) | optional | Newest point. Defaults to now. |
count | integer | optional | Cap on number of emails (newest-first). 0/omitted = no cap; bounded only by the window. Clamped to a server maximum (default 1000). |
folders | object | optional | Include non-INBOX folders: { "sent": false, "spam": false, "trash": false }. |
Modes:
- Window mode — give
window_start(and optionallywindow_end) to import everything in that range. - Count mode ("last N") — give just
countto import the most recent N emails.window_enddefaults to now; the walk is bounded by a generous lookback. - Both — a window that is also capped at
countemails.
curl -X POST "https://<your-host>/v1/{mailerID}/backfill" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"window_start": "2025-01-01T00:00:00Z",
"count": 1000,
"folders": { "sent": true }
}'
{ "success": true, "jobId": "01JC3BBW8S9YGX2VNKG5MD7BTA" }
Idempotent per mailer: only one active backfill per mailer at a time. A second request while one is in flight returns 409 with the existing jobId:
{ "success": false, "error": "active backfill already exists", "jobId": "01JC3BBW8S9YGX2VNKG5MD7BTA" }
Check status
GET /v1/{mailerID}/backfill/{jobId}
{
"success": true,
"jobId": "01JC3BBW8S9YGX2VNKG5MD7BTA",
"status": "processing",
"processed": 250,
"target": 1000,
"percent": 25,
"startedAt": "2026-06-03T10:07:23Z",
"endedAt": ""
}
status—pending→processing→done(orfailed/cancelled).percent— time-based progress over the requested window (clamped 0–100;100when done).processed/target— emails delivered so far / the count cap (0= uncapped).endedAt— set once the job reaches a terminal state.
Abort a backfill
POST /v1/{mailerID}/backfill/{jobId}/abort
Cancels an in-flight job; the worker stops cleanly at its next slice, leaving the job cancelled. Already-finished jobs (done/failed/cancelled) return 409.
{ "success": true, "jobId": "01JC3BBW8S9YGX2VNKG5MD7BTA", "status": "cancelled" }
After a job is cancelled (or finished), you can start a new backfill for the same mailer.
Webhook delivery & dedupe
- Each historical email is delivered as a normal incoming-email webhook (
IMAP_OAUTH), with the full email content inline. - Backfill deliveries carry an
X-Backfill-Job-Idheader so you can distinguish them from live mail. - Because delivery is at-least-once, dedupe by
Message-Idon your side — this is required, not optional.
Notes & limits
- Gmail only (for now): see the provider-support note above.
- Throughput is bounded by Gmail's per-user API quota (~3,000 messages/min) and by your webhook endpoint's speed; large mailboxes take minutes. Use the status endpoint to track progress.
- Window precision is rounded to whole seconds (Gmail's date filter is day-granular).