Managing Email Migrations via the API
Manage email migrations through the TrekMail API. Test connections, start imports, monitor progress, cancel, retry, and delete migration jobs.
Article details
Type, difficulty, plans, and last updated info.
▼
Article details
Type, difficulty, plans, and last updated info.
- Type
- Reference
- Difficulty
- Intermediate
- Plans
- Starter · Pro · Agency
- Last updated
- Apr 29, 2026
The Migration API lets you import email from any IMAP provider into a TrekMail mailbox — programmatically. You can test connections, start imports, monitor per-folder progress, cancel running jobs, retry failures, and clean up old records.
Before you start
- You need a Starter plan or higher. The Nano plan does not include the migration tool.
- Pro and Agency can start, cancel, retry, and delete migrations through the API (
migrations:read+migrations:write). Starter can read migrations via the API and run new ones from the dashboard. - One migration can run per account at a time. Start a new one after the current one finishes or cancel it first.
Scopes
| Scope | What it does | Plans |
|---|---|---|
migrations:read |
List migrations, view migration details | Starter · Pro · Agency |
migrations:write |
Test connections, start, cancel, retry, delete | Pro · Agency |
Endpoints
Test connection
POST /api/v1/migrations/test-connection
Scope: migrations:write
Validates IMAP credentials and returns a list of source folders with message counts. Use this before starting a migration to verify the connection works and let the user choose which folders to import.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
source_host |
string | Yes | IMAP server hostname (e.g., imap.gmail.com) |
source_port |
integer | Yes | IMAP port (typically 993 for SSL) |
source_security |
string | Yes | ssl, tls, or none |
source_email |
string | Yes | Email address on the source server |
source_username |
string | No | Username if different from email |
source_password |
string | Yes | Password or app password |
Response (success):
{
"success": true,
"folders": {
"INBOX": 1234,
"Sent": 567,
"Drafts": 12,
"Work": 89
}
}
Response (failure): 422 with connection_failed error code.
List migrations
GET /api/v1/migrations
Scope: migrations:read
Returns a paginated list of migration jobs for your account.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status |
string | Filter by status (pending, validating, planning, processing, completed, failed, cancelled) |
mailbox_id |
integer | Filter by target mailbox |
per_page |
integer | Results per page (default: 20, max: 100) |
Get migration
GET /api/v1/migrations/{id}
Scope: migrations:read
Returns detailed migration status including per-folder progress breakdown.
Response:
{
"data": {
"id": 5,
"mailbox_id": 10,
"mailbox_email": "support@acme.com",
"provider": "gmail",
"source_host": "imap.gmail.com",
"source_email": "j***e@gmail.com",
"status": "processing",
"progress": 45,
"total_messages": 1234,
"imported_messages": 556,
"failed_messages": 2,
"skipped_duplicates": 12,
"selected_folders": ["INBOX", "Sent"],
"import_since": "2025-01-01",
"skip_duplicates": true,
"folders": [
{ "name": "INBOX", "status": "processing", "expected": 1000, "imported": 450, "failed": 2, "skipped": 10 },
{ "name": "Sent", "status": "pending", "expected": 234, "imported": 0, "failed": 0, "skipped": 0 }
],
"error_message": null,
"poll_hint_seconds": 10,
"started_at": "2026-03-13T10:00:00+00:00",
"finished_at": null,
"created_at": "2026-03-13T09:59:50+00:00"
}
}
poll_hint_seconds tells you how often to poll for updates: 5 seconds during pending/validating/planning, 10 seconds during processing, null for terminal states.
source_email is masked for security (e.g., j***e@gmail.com).
Start migration
POST /api/v1/migrations
Scope: migrations:write
Starts a new email migration. Only one migration can run per account at a time.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
mailbox_id |
integer | Yes | Target TrekMail mailbox ID |
provider |
string | Yes | gmail, outlook, yahoo, icloud, or generic_imap |
source_host |
string | Yes | IMAP server hostname |
source_port |
integer | Yes | IMAP port |
source_security |
string | Yes | ssl, tls, or none |
source_email |
string | Yes | Source email address |
source_username |
string | No | Username if different from email |
source_password |
string | Yes | Source password or app password |
selected_folders |
string[] | No | Specific folders to import (default: all) |
import_since |
date | No | Only import emails after this date |
skip_duplicates |
boolean | No | Skip duplicate messages (default: true) |
Response: 201 with the migration job resource.
Error responses:
| Status | Code | Meaning |
|---|---|---|
409 |
conflict | An active migration is already running on this account |
503 |
migration_capacity_reached |
Server-wide migration limit reached (retryable) |
422 |
validation_error | Invalid parameters or mailbox not found |
Cancel migration
POST /api/v1/migrations/{id}:cancel
Scope: migrations:write
Cancels a running migration. The migration must be in an active state (pending, validating, planning, or processing).
Retry migration
POST /api/v1/migrations/{id}:retry
Scope: migrations:write
Retries a failed or cancelled migration. Resets progress to 0 and re-enters the validation pipeline.
Returns 409 if another migration is already running on the account.
Automatic resume of partial completions
A migration that ends completed with meta.completed_partially=true (imported less than 95% of total, typically due to source-side throttling) is auto-resumed by the migrations:auto-resume-partial cron on a two-tier schedule:
- Quick phase — up to 5 attempts, 1h apart (configurable via
MIGRATION_AUTO_RESUME_MAX_ATTEMPTSandMIGRATION_AUTO_RESUME_COOLDOWN_MINUTES). - Daily phase — triggered only if the quick phase made progress; up to 3 more attempts spaced 24h apart (
MIGRATION_AUTO_RESUME_DAILY_*). Lets daily IMAP quotas reset. - Zero-progress detection — if an attempt yields no new messages, the job is marked
meta.likely_permanentand auto-resume stops (cause is almost always auth, not throttling).
Each event is logged as migration.auto_resume.dispatched / phase_transitioned / likely_permanent. Manual /api/v1/migrations/{id}:retry (or the UI Continue button) clears all auto-resume state and restarts the state machine.
Delete migration
DELETE /api/v1/migrations/{id}
Scope: migrations:write
Deletes a migration record. The migration must not be running (cancel it first).
Returns 204 No Content on success.
Rate limits
Migration write operations have a dedicated rate limit of 10 requests per minute per token, separate from the standard API rate limit.
Additionally, the server enforces a global concurrency limit (default: 20 simultaneous migrations). When the limit is reached, new migration requests return 503 with migration_capacity_reached and retryable: true. Wait a few minutes and retry.
Audit events
All migration API actions are recorded in the audit log:
- migration_started — a new migration was initiated
- migration_cancelled — a running migration was cancelled
- migration_retried — a failed or cancelled migration was retried
- migration_deleted — a migration record was deleted
MCP tools
The same migration capabilities are available through the MCP server as 16 tools — 7 for single migrations (test_migration_connection, list_migrations, get_migration, start_migration, cancel_migration, retry_migration, delete_migration) and 9 for bulk migrations (preview_bulk_migration, start_bulk_migration, list_bulk_migrations, get_bulk_migration, cancel_bulk_migration, retry_bulk_migration, resume_bulk_migration, delete_bulk_migration, update_bulk_migration_job_password). Migration write tools require the TREKMAIL_ALLOW_MIGRATION=true environment variable and per-call confirmation parameters. See Connecting AI Agents (MCP) for details.
Bulk Migration API
The Bulk Migration API lets you migrate many accounts at once using a CSV-style data payload. See Bulk Email Migration for the user guide and Bulk Migration CSV Format for the data format.
Endpoints
| Method | Endpoint | Scope | Description |
|---|---|---|---|
| POST | /api/v1/migrations/bulk/preview |
migrations:write |
Preview & validate CSV data |
| POST | /api/v1/migrations/bulk |
migrations:write |
Start a bulk migration batch |
| GET | /api/v1/migrations/bulk |
migrations:read |
List bulk migration batches |
| GET | /api/v1/migrations/bulk/{id} |
migrations:read |
Get batch details with per-job status |
| POST | /api/v1/migrations/bulk/{id}:cancel |
migrations:write |
Cancel entire batch |
| POST | /api/v1/migrations/bulk/{id}:retry |
migrations:write |
Retry failed jobs in batch |
| POST | /api/v1/migrations/bulk/{id}:resume |
migrations:write |
Resume paused batch |
| DELETE | /api/v1/migrations/bulk/{id} |
migrations:write |
Delete batch record |
| PATCH | /api/v1/migrations/bulk/{id}/jobs/{job}/password |
migrations:write |
Update source password for a failed job |
Preview request
POST /api/v1/migrations/bulk/preview
Scope: migrations:write
| Field | Type | Required | Description |
|---|---|---|---|
data |
string | Yes | CSV data (one row per line) |
provider |
string | No | gmail, outlook, yahoo, icloud, generic_imap |
source_host |
string | No | IMAP host (if provider is generic_imap) |
source_port |
integer | No | IMAP port (default 993) |
source_security |
string | No | ssl, tls, none |
per_row_server |
boolean | No | Each row has its own server settings (6-column format) |
Response includes categorized rows (valid, invalid_source_email, invalid_destination, etc.), plan limits, time estimate, and storage info.
Start batch request
POST /api/v1/migrations/bulk
Scope: migrations:write
Same fields as preview, plus:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Batch name (auto-generated if empty) |
folder_strategy |
string | No | all, standard, inbox_only (default: all) |
import_since |
string | No | Date filter (YYYY-MM-DD) |
skip_duplicates |
boolean | No | Skip duplicate messages (default: true) |
idempotency_key |
string | No | Client-provided idempotency key |
Concurrency limits
| Plan | Max rows per batch | Concurrent per account |
|---|---|---|
| Starter | 100 | 2 |
| Pro | 300 | 5 |
| Agency | 1,000 | 10 |
The global server limit (20 concurrent migrations) is shared between single and bulk migrations.
MCP tools
Six bulk migration tools are available via MCP: preview_bulk_migration, start_bulk_migration, list_bulk_migrations, get_bulk_migration, cancel_bulk_migration, retry_bulk_migration. Write tools require TREKMAIL_ALLOW_MIGRATION=true.
Quick fixes
- 403 "insufficient_scope": Your token needs
migrations:readormigrations:write. Create a new token with the correct scopes. - 403 "token_scope_blocked_by_plan": Migration scopes require a paid plan (Starter or higher).
- 409 "active migration running": Cancel the existing migration or wait for it to finish.
- 503 "migration_capacity_reached": The server is at capacity. Retry in a few minutes.
- 422 on test-connection: Check your IMAP credentials, hostname, port, and security setting.
Related articles
Jump to nearby guides that continue the workflow.