---
title: "Start migrating a domain from your server"
method: POST
path: "/customers/{id}/domains/{domain}/migrations"
tags: ["Partners"]
---

# Start migrating a domain from your server

`POST /customers/{id}/domains/{domain}/migrations`

## Request

- Base URL: `https://faivelo.com/api/partner`
- URL: `https://faivelo.com/api/partner/customers/{id}/domains/{domain}/migrations`
- Auth: HTTP bearer

Creates the mailboxes that do not exist yet (password = the one in the file unless `newPassword` is given; existing mailboxes keep theirs), stores the source passwords encrypted and held, and queues the first copy. At most 500 rows. Pass `expectedBytes` from the test to be refused (422, `STORAGE_DOES_NOT_FIT`) before anything is created when the mail does not fit the customer's storage. Idempotent per address: the same rows again only refresh the stored source passwords and queue another sync. 403 when the domain is not verified yet. Requires `partner:write`.

## Path parameters

- `id` string, required
- `domain` string, required

## Request body

- PartnerMigrationRequest: The source server and the mailboxes, as CSV text or JSON rows (one of `csv` and `rows`)
  - `source` PartnerMigrationSource, required
    - `host` string, required
    - `port` integer
    - `tls` boolean
  - `csv` string: `email,password[,newPassword]`, one mailbox per line; an optional header line; cells may be double-quoted
  - `rows` object[]
    - `email` string, required
    - `password` string, required: The current password on the source server
    - `newPassword` string, nullable: The password the Faivelo mailbox gets; default: the same as `password`
  - `expectedBytes` object: Bytes per source address from the test, so create can refuse what does not fit before creating anything

## Response `201`

The migration with its first progress, and what happened per row

- object
  - `success` boolean
  - `data` PartnerMigrationCreated
    - `migration` PartnerMigration
      - `id` string
      - `domain` string
      - `source` PartnerMigrationSource
        - `host` string, required
        - `port` integer
        - `tls` boolean
      - `status` 'PENDING' | 'RUNNING' | 'COMPLETED' | 'PARTIAL' | 'FAILED'
      - `phase` 'copying' | 'idle' | 'finishing' | 'finished': copying: a pass is queued or running; idle: waiting for you to sync again or finish; finishing: the last pass is queued; finished: the source credentials are deleted
      - `credentialsHeld` boolean: True until the domain is finished: the source passwords are still stored (encrypted) so sync works
      - `createdAt` string, date-time
      - `startedAt` string, date-time, nullable
      - `completedAt` string, date-time, nullable
      - `finishRequestedAt` string, date-time, nullable: When finish was requested; the last pass is running until finishedAt
      - `finishedAt` string, date-time, nullable
      - `mailboxes` PartnerMigrationMailbox[]
        - `sourceEmail` string
        - `destAddress` string
        - `status` 'PENDING' | 'RUNNING' | 'COMPLETED' | 'PARTIAL' | 'FAILED'
        - `totalMessages` integer
        - `copiedMessages` integer
        - `failedMessages` integer
        - `bytesCopied` integer
        - `lastSyncedAt` string, date-time, nullable
        - `syncPending` boolean: Another pass is queued or running for this mailbox
        - `error` string, nullable
      - `totals` object
        - `mailboxes` integer
        - `running` integer
        - `completed` integer
        - `failed` integer
        - `totalMessages` integer
        - `copiedMessages` integer: Accumulates across sync passes, so it can exceed totalMessages
        - `failedMessages` integer
        - `bytesCopied` integer
        - `syncPending` integer: Mailboxes with a pass queued or running
        - `syncedUpTo` string, date-time, nullable: The domain is synced up to here: the oldest last pass among its mailboxes
    - `queued` integer: Mailboxes queued for the first copy
    - `rows` object[]
      - `line` integer
      - `email` string
      - `mailbox` 'created' | 'existing'
      - `action` 'added' | 'refreshed'
    - `failed` object[]: Rows whose mailbox could not be created; the rest went ahead
      - `line` integer
      - `email` string
      - `error` string

## Other responses

- `400`: Invalid rows (every problem listed under `problems`)
- `401`: Missing or invalid API key, or not a partner key
- `403`: The key lacks the required scope, or provisioning is refused (partner paused, customer cap reached)
- `404`: Not found, including any customer that is not yours
- `409`: Sandbox customer
- `422`: The mail does not fit, or no mailbox could be set up
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/faivelo/apis/faivelo-api.md) · [All operations](https://skmtc.dev/faivelo/apis/faivelo-api/llms.txt) · [OpenAPI document](https://skmtc.dev/faivelo/apis/faivelo-api/revisions/a7413ddea5d1?raw)
