MisarMisar Docs
MisarMailMisar.BlogMisarReachMisarPostMisar.DevMisarSEOMisar PlatformMisar SSO
API Reference

Email Sync

Trigger manual IMAP sync for connected email accounts to fetch latest messages

MisarMail syncs connected IMAP accounts automatically in the background. The sync API lets you trigger an immediate sync on demand — for example, when a user clicks a "Refresh" button in your inbox UI.

Requires an active dashboard session. These routes are cookie-authenticated and are not part of the msk_ API-key surface.

Trigger a sync

POST/api/sync

Trigger an immediate IMAP sync for one connected account.

Request body

accountIdstringbodyrequired

UUID of the IMAP account to sync. The account must belong to the current user and have IMAP configured.

folderstringbodydefault: INBOX

Mailbox folder to sync. Must be an allowed IMAP folder name (e.g. INBOX, Sent, Drafts, Trash, Spam, Archive, Junk, or their INBOX.* / [Gmail]/* variants).

fullSyncbooleanbodydefault: false

When true, fetch the full folder history instead of only messages since the last sync.

limitintegerbodydefault: 100

Maximum number of messages to fetch (1–200).

Response fields

successboolean

true when the sync completed.

messagestring

Human-readable status ("Sync completed").

statsobject

Sync results: newEmails (number of messages inserted), folder (the folder that was synced), and syncedAt (ISO-8601 timestamp).

curl -X POST /api/sync \
  -H "Content-Type: application/json" \
  -d '{ "accountId": "3f8a1c2e-0b4d-4e6f-9a1b-2c3d4e5f6a7b" }'
async function handleRefreshClick(accountId: string) {
  const res = await fetch('/api/sync', {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ accountId }),
  });

  if (res.status === 409) {
    // Sync already in progress — treat as a benign skip, not an error.
    return;
  }

  const { stats } = await res.json();
  if (stats?.newEmails > 0) {
    // reload inbox list
  }
}
{
  "success": true,
  "message": "Sync completed",
  "stats": {
    "newEmails": 4,
    "folder": "INBOX",
    "syncedAt": "2025-06-14T11:05:30Z"
  }
}

Concurrent syncs

Only one sync can run per account at a time. Before starting, the endpoint atomically claims the account by setting its sync_status to syncing. If the account is already mid-sync (for example, the background worker or a concurrent "Sync All" claimed it first), the request is rejected:

{
  "success": false,
  "error": "Sync already in progress",
  "alreadySyncing": true
}

409 with alreadySyncing: true is a benign skip, not a failure — another sync is already covering this account. A stuck syncing claim self-heals: if a sync has been marked syncing for more than 5 minutes (e.g. a crashed run), the next request is allowed to reclaim it.

Do not poll this endpoint. Use it only in response to an explicit user action (e.g. a refresh-button click). Continuous polling collides with the background sync and the concurrency guard.

Other responses

StatusBodyCause
400{ success: false, error: "Validation failed", details }Invalid body (bad accountId, unknown folder, out-of-range limit)
400{ success: false, error: "Account does not have IMAP configured" }Account has no IMAP host or stored credentials
401{ success: false, error: "Unauthorized" }No active session
404{ success: false, error: "Account not found" }No such account for this user
500{ success: false, error: "<message>" }IMAP connection/auth failure or unexpected error

Get sync status

GET/api/sync

Returns the current sync state and per-folder message counts for a single account.

Query parameters

accountIdstringqueryrequired

UUID of the account to inspect. Returns 400 if omitted.

Response fields

successboolean

true when the request succeeded.

accountobject

The account's sync state: id, email, lastSyncAt, and syncStatus.

statsobject

folderCounts — a map of folder name to { total, unread } (only folders with messages are included) — and totalUnread, the sum of unread counts across folders.

curl "/api/sync?accountId=3f8a1c2e-0b4d-4e6f-9a1b-2c3d4e5f6a7b" \
  -H "Cookie: <session>"
{
  "success": true,
  "account": {
    "id": "3f8a1c2e-0b4d-4e6f-9a1b-2c3d4e5f6a7b",
    "email": "hello@example.com",
    "lastSyncAt": "2025-06-14T11:05:30Z",
    "syncStatus": "idle"
  },
  "stats": {
    "folderCounts": {
      "inbox": { "total": 128, "unread": 3 },
      "sent": { "total": 42, "unread": 0 }
    },
    "totalUnread": 3
  }
}

syncStatus values:

ValueMeaning
idleLast sync completed successfully; no active sync
syncingA sync is currently in progress
pendingFreshly provisioned account not yet synced
errorLast sync attempt failed (check IMAP credentials or server availability)

Auto-sync behaviour

TriggerBehaviour
Background auto-syncConnected accounts are synced automatically on a periodic schedule
Trigger via POST /api/syncImmediate; blocked by the per-account concurrency guard while a sync is running
New account connectedSync triggered automatically

For new accounts (or a fullSync), MisarMail fetches recent history; otherwise it fetches messages since lastSyncAt (with a short look-back buffer to catch re-filed or back-dated messages). Synced messages are de-duplicated on (account_id, message_id), so re-running a sync is idempotent.