Skip to main content

Overview

Copies a message to a destination mailbox. Supports both same-account copy (using native IMAP COPY) and cross-account copy (using FETCH + APPEND). The original message remains in the source mailbox unchanged.
This tool requires MAIL_IMAP_WRITE_ENABLED=true to be set in your environment variables. Write operations are disabled by default as a safety measure.

Common use cases

  • Archive messages to a different folder
  • Copy messages between accounts
  • Backup important messages
  • Duplicate messages to multiple mailboxes
  • Migrate messages across IMAP servers

Input parameters

body.account_id
string
default:"default"
Source account identifier. Must match pattern ^[A-Za-z0-9_-]{1,64}$.
body.message_id
string
required
Message identifier in format imap:{account_id}:{mailbox}:{uidvalidity}:{uid}.The account_id in the message_id must match the account_id parameter.
body.destination_mailbox
string
required
Destination mailbox name (1-256 characters). Must not contain control characters.Common mailbox names:
  • INBOX - Main inbox
  • Sent or Sent Items - Sent messages
  • Archive - Archived messages
  • Trash or Deleted Items - Deleted messages
The exact mailbox name depends on your IMAP server configuration. Use imap_list_mailboxes to see available mailboxes.
body.destination_account_id
string
Destination account identifier. Defaults to the source account_id (same-account copy).When copying to a different account:
  • The message is fetched from the source account
  • The raw RFC822 message is appended to the destination account
  • Both accounts must be configured in environment variables

Response

summary
string
Human-readable one-line outcome: "Message copied"
data
object
meta
object

Example request

{
  "account_id": "default",
  "message_id": "imap:default:INBOX:1234567890:42",
  "destination_mailbox": "Archive"
}

Example response

{
  "summary": "Message copied",
  "data": {
    "status": "ok",
    "issues": [],
    "source_account_id": "default",
    "destination_account_id": "default",
    "source_mailbox": "INBOX",
    "destination_mailbox": "Archive",
    "message_id": "imap:default:INBOX:1234567890:42",
    "new_message_id": null,
    "steps_attempted": 2,
    "steps_succeeded": 2
  },
  "meta": {
    "now_utc": "2026-03-03T22:30:15.123Z",
    "duration_ms": 456
  }
}

Error responses

{
  "error": {
    "code": "invalid_input",
    "message": "write tools are disabled; set MAIL_IMAP_WRITE_ENABLED=true",
    "details": {}
  },
  "meta": {
    "now_utc": "2026-03-03T22:30:15.123Z",
    "duration_ms": 5
  }
}

Implementation notes

Same-account copy

For copies within the same account, the server uses the native IMAP UID COPY command:
  1. Connect to the source account and authenticate (steps_attempted=1)
  2. Select the source mailbox in read-write mode and verify uidvalidity
  3. Execute UID COPY command (steps_attempted=2)

Cross-account copy

For copies between different accounts, the server uses FETCH + APPEND:
  1. Connect to source account and authenticate (steps_attempted=1)
  2. Select source mailbox (read-only) and verify uidvalidity
  3. Fetch the raw RFC822 message from source (steps_attempted=2)
  4. Connect to destination account and authenticate (steps_attempted=3)
  5. Append the message to destination mailbox (steps_attempted=4)

Step tracking

The steps_attempted and steps_succeeded fields help diagnose where failures occur:
  • Same-account: 2 steps total (connect + copy)
  • Cross-account: 4 steps total (connect source + fetch + connect destination + append)
If steps_succeeded < steps_attempted, check the issues array to identify which stage failed.

Message ID availability

  • Same-account copies may not return new_message_id if the IMAP server doesn’t support the UIDPLUS extension
  • Cross-account copies using APPEND typically don’t return new_message_id
  • To find the copied message, search the destination mailbox using message headers or content

See also

Build docs developers (and LLMs) love