> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brudcast.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a migration

> Connect your old provider, choose what to bring across, check the preview, start the run and review what came across.

export const DemoVideo = ({id, title, embedUrl, src, poster, duration}) => {
  if (embedUrl) {
    return <figure className="brd-media" data-media-id={id}>
        <iframe className="brd-media-frame w-full aspect-video" src={embedUrl} title={title} allow="accelerometer; autoplay; clipboard-write; encrypted-media; picture-in-picture" allowFullScreen />
      </figure>;
  }
  if (src) {
    return <figure className="brd-media" data-media-id={id}>
        <video className="brd-media-frame w-full aspect-video" src={src} poster={poster} controls playsInline preload="metadata" />
      </figure>;
  }
  const label = duration ? `Video walkthrough coming soon · ${duration}` : "Video walkthrough coming soon";
  return <Placeholder id={id} kind="video" label={label} description={title} ratio="16 / 9" icon={<PlayIcon />} />;
};

export const Loop = ({id, src, alt, caption}) => {
  if (!src) {
    return <Placeholder id={id} kind="loop" label="Animation coming soon" description={alt} icon={<PlayIcon />} />;
  }
  return <figure className="brd-media" data-media-id={id}>
      <video className="brd-media-frame" src={src} autoPlay muted loop playsInline aria-label={alt} />
      {caption && <figcaption className="brd-media-caption">{caption}</figcaption>}
    </figure>;
};

export const Screenshot = ({id, src, srcDark, alt, caption}) => {
  if (!src) {
    return <Placeholder id={id} kind="screenshot" label="Screenshot coming soon" description={alt} icon={<ImageIcon />} />;
  }
  return <figure className="brd-media" data-media-id={id}>
      <img className="brd-media-frame block dark:hidden" src={src} alt={alt} />
      <img className="brd-media-frame hidden dark:block" src={srcDark || src} alt={alt} />
      {caption && <figcaption className="brd-media-caption">{caption}</figcaption>}
    </figure>;
};

Move your contacts, lists, suppressions, templates, domains and senders into Brudcast in one run.

<Screenshot id="ss-migrations-run-a-migration-hero" alt="A finished migration from Mailchimp showing every kind Done, with What to do next below" />

<Info>
  **Before you start:**

  * Get an API key from your old provider. See [Supported providers](#supported-providers) for
    which fields you need and where to find them.
  * Check your plan's contact allowance. Contacts beyond it aren't imported.
  * Keep your old account active until you've finished [after you migrate](#after-you-migrate).
</Info>

<Accordion title="Watch the walkthrough" icon="circle-play">
  <DemoVideo id="V15" title="Migrate from another provider" duration="3 min" />
</Accordion>

<Steps>
  <Step title="Pick your provider">
    Go to **Migrations**. Under **Where are you moving from?**, select the service you use today.
    Type in **Search providers...** to find it quickly.

    <Screenshot id="ss-migrations-run-a-migration-01-source" alt="The provider grid with Mailchimp highlighted under Marketing email" />
  </Step>

  <Step title="Connect it">
    Paste the credentials the form asks for, and choose the region if it asks. Select **Connect**.

    Brudcast checks the key, then counts what your account holds. A large account can take a
    minute. Nothing is written yet.

    <Screenshot id="ss-migrations-run-a-migration-02-connect" alt="The Connect Mailchimp step with the API Key field and the Where to find them link" />
  </Step>

  <Step title="Choose what to bring across">
    Under **Choose what to bring across**, select the kinds you want. Each shows how many items were
    found. Kinds the provider can't supply are listed underneath, with the reason.

    * **Suppressions** stays selected while **Contacts** is, so addresses that bounced or
      unsubscribed at your old provider stay suppressed here.
    * Without **Custom fields**, the values in those fields are dropped.
    * Without **Lists**, contacts arrive without their list memberships.

    When contacts are included, set how to treat them:

    * **Contacts you already have:** **Leave them untouched** imports only people who are new here.
      **Update them** overwrites names and field values with what your old provider holds.
    * **Tag imported contacts (optional):** every contact the migration writes gets these tags, so
      you can find them as a group later.
    * **List name prefix (optional):** added to the start of each imported list's name, to keep
      imported lists apart from ones you built here.

    <Screenshot id="ss-migrations-run-a-migration-03-choose" alt="Choose what to bring across with Contacts, Lists and Suppressions selected and the duplicate options below" />
  </Step>

  <Step title="Confirm and start">
    **Confirm and start** shows what the migration will create. For contacts it splits new people
    from ones you already have, and leaves out suppressed addresses.

    For a large account the new-and-existing split is worked out from the first 2,000 contacts; the
    run reports the real figures as it goes.

    Tick **I have permission to email these contacts, and bringing them over from your provider
    does not change that.** Then select **Start migration**.

    <Screenshot id="ss-migrations-run-a-migration-04-confirm" alt="Confirm and start with the preview counts, the consent checkbox and the Start migration button" />
  </Step>

  <Step title="Watch the progress">
    The migration page shows **Progress**, one row per kind, in the order they run. Each moves from
    **Waiting** to **Running** to **Done**. The numbers update live.

    You can leave the page. The run carries on, and a notification tells you when it's finished.

    <Loop id="lp-migrations-run-a-migration-progress" alt="Resource rows moving from Waiting to Running to Done as counts climb" />
  </Step>

  <Step title="Review what came across">
    When the run finishes, the section becomes **What came across**. Each row shows how many items
    were imported, how many were already here or suppressed, and how many failed.

    If any items failed, download that row's error report. It's a CSV listing each item and the
    reason.

    **What to do next** lists the follow-up work, such as publishing DNS for imported domains. See
    [After you migrate](#after-you-migrate).
  </Step>
</Steps>

## What happens next

When the run ends you get a notification: **Migration complete**, with the number of records
imported, or **Migration failed**. Select **View migration** to open it.

Your provider key is erased at this point. To run another migration, connect again.

## Cancel a migration

While a migration is queued or running, you can stop it. Select the cancel option on the migration
page, then confirm in **Cancel migration run**. Select **Keep Running** to change your mind.

The run stops at the next safe point, after the batch it's working on. Everything already imported
stays.

## What comes across

A migration only **reads** from your old provider. Nothing there is changed or deleted, and nothing
is written here until you select **Start migration**: the **Confirm and start** step previews every
kind first.

| Kind                  | What you get in Brudcast                                                       |
| --------------------- | ------------------------------------------------------------------------------ |
| **Custom fields**     | Contact custom fields, matching the old provider's definitions                 |
| **Lists**             | Contact lists with the same names, plus an optional prefix                     |
| **Suppressions**      | Bounced, complained and unsubscribed addresses, added to your suppression list |
| **Contacts**          | Contacts with their names, custom field values and list memberships            |
| **Templates**         | Email templates, with merge tags translated                                    |
| **Sending domains**   | Sending domains, ready for you to publish Brudcast's DNS records               |
| **Sender identities** | Sender addresses, or SMS sender IDs, ready to use once their domain verifies   |

Campaign history, reports, engagement data, API keys and SMTP passwords stay with your old provider.

The kinds always run in that order, so each can use the ones before it: custom fields and lists come
before contacts, so contacts land with their values and memberships, and suppressions come before
contacts, so an address your old provider saw bounce is never brought back as a contact you can
mail. One kind failing doesn't stop the others, and cancelling keeps what's already imported.

Your provider key is stored encrypted and erased when the run finishes, so a finished migration
can't be replayed against your old account. Contacts you already have are matched by email address,
and you choose whether to leave them untouched or update them.

| Status               | Meaning                                                  |
| -------------------- | -------------------------------------------------------- |
| **Not Started**      | Connected, but not started yet                           |
| **Queued**           | Started and waiting to run                               |
| **Running**          | Reading from your old provider and writing into Brudcast |
| **Completed**        | Every kind finished                                      |
| **Partly Completed** | Some kinds finished and some failed                      |
| **Failed**           | No kind finished                                         |
| **Cancelled**        | You stopped it                                           |

Each kind has its own status too: **Waiting**, **Running**, **Done**, **Skipped** or **Failed**.

## Supported providers

You can migrate from 11 providers, grouped in the dashboard as **Marketing email**,
**Transactional email** and **SMS**.

| Provider  | Kind          | Credentials                                          | Where to find them                                                                                   |
| --------- | ------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Brevo     | Marketing     | **API Key (v3)**                                     | SMTP & API keys. Create a v3 key; an SMTP password won't work                                        |
| Mailchimp | Marketing     | **API Key**                                          | Account API keys. The key ends in a data center suffix such as `-us14`                               |
| Mailgun   | Marketing     | **Private API Key**, **Region**                      | API security. Use the private key; a sending key won't work                                          |
| Mailjet   | Marketing     | **API Key**, **API Secret**                          | API keys, which Mailjet calls the public and private keys                                            |
| SendGrid  | Marketing     | **API Key**                                          | API keys. It needs read access to Marketing, Suppressions, Template Engine and Sender Authentication |
| SendPulse | Marketing     | **API ID**, **API Secret**                           | API settings. You need both values                                                                   |
| Mandrill  | Transactional | **API Key**                                          | Mandrill settings, not your Mailchimp account key                                                    |
| Postmark  | Transactional | **Server API Token**, **Account API Token**          | API tokens. The server token reads templates; the account token reads domains and sender signatures  |
| SparkPost | Transactional | **API Key**, **Region**                              | API keys. It needs read access to suppression lists, templates and sending domains                   |
| ZeptoMail | Transactional | **Send Mail Token**, **Mail Agent Alias** (optional) | The ZeptoMail API help. Leave the alias blank to read every mail agent the token can reach           |
| Termii    | SMS           | **API Key**                                          | The Termii dashboard, API tab                                                                        |

Select **Where to find them** on the **Connect** step to open the provider's own page.

Not every provider can supply every kind. Transactional services (Mandrill, Postmark, SparkPost,
ZeptoMail) send on behalf of an app and hold no contact lists, so they bring suppressions, templates
and domains only; Mandrill is Mailchimp's transactional service, so migrate from Mailchimp to bring
contacts across. Mailchimp and SparkPost verify a whole sending domain rather than individual from
addresses, so their domains come across instead of a sender list. Mailgun and ZeptoMail set the from
address on each message, so add your senders once the domain is verified. Termii phonebooks are
keyed on phone numbers while Brudcast identifies imported contacts by email address, so they arrive
as empty lists, and your registered sender IDs come across as SMS sender IDs.

<Warning>
  **Mailgun and SparkPost:** choose the region (**US** or **EU**) your account was created in. The
  wrong region looks exactly like a wrong key, and the connection fails.
</Warning>

## Merge tags from your old provider

Every provider writes personalization differently, so imported templates have their merge tags
rewritten into Brudcast's `{{ tag }}` syntax, in the subject, HTML and plain text. Mailchimp's
`*|FNAME|*`, Mailjet's `[[data:firstname]]`, Mailgun's `%recipient.first_name%` and Brevo's
`{{ contact.FIRSTNAME }}` all become `{{first_name}}`, and Mailgun's `%recipient%` becomes
`{{email}}`. A leading `recipient.`, `contact.` or `subscriber.` is dropped, the name is lowercased,
and spaces, dots and hyphens become underscores, so `COMPANY_NAME` becomes `{{company_name}}`. A
default value, such as Mailjet's `[[data:firstname:"there"]]`, is dropped: Brudcast has no fallback
values, and an empty field renders as empty text.

A tag whose name can't become a valid Brudcast tag is left exactly as the provider wrote it. That
covers Mailchimp system tags with a colon, such as `*|MC:SUBJECT|*`, and logic or loops such as
`{{#if plan}}` and `{{#each items}}`, which have no equivalent here and need rewriting by hand.
They're left visible on purpose: a tag you can see in the editor is easy to fix, while one that
quietly rendered as nothing would reach your recipients unnoticed.

Built-in tags are filled per recipient, as is any tag matching one of your custom field keys. Every
other tag becomes a fill-once placeholder you set when you use the template. See
[Placeholders and merge tags](/templates/placeholders-and-merge-tags).

## After you migrate

A migration brings your data across. A few things still need you.

<Steps>
  <Step title="Publish DNS for your sending domains">
    Imported sending domains start unverified, whatever their state at your old provider, because
    verification means Brudcast's own records are published. Open each domain under
    **Channels > Email**, publish the records it shows at your DNS host, and verify. Keep your old
    provider's records in place until you've fully moved. See
    [Verify your domain](/channels/email/verify-your-domain).
  </Step>

  <Step title="Check your senders and contacts">
    Imported email senders can send as soon as their domain verifies. SMS sender IDs from Termii
    arrive pending, because every sender ID needs Brudcast's own approval; see
    [Sender IDs](/channels/sms/sender-ids). List memberships came across as they were, suppressed
    addresses were left out, and Termii phonebooks arrive as empty lists you can fill with a
    [CSV import](/contacts/importing).
  </Step>

  <Step title="Review your templates and suppressions">
    Imported templates open with their merge tags rewritten. Look for tags left as they were, such
    as logic blocks, and for fill-once placeholders you didn't expect. Bounces, complaints and
    unsubscribes from your old provider are in **Contacts > Suppressions** with the source `import`.
    Leave them there: they're the reason your old reputation was healthy.
  </Step>

  <Step title="Switch your app over">
    If your app sent email through your old provider, create a
    [sending key](/channels/email/domain-api-keys) and send to
    `POST https://mailing-service.prod.brudcast.com/api/v1/send`, or create an
    [SMTP user](/channels/email/smtp-users) and point your app at `out-smtp.prod.brudcast.com`. Move
    one kind of mail at a time, such as receipts first, and watch the results.
  </Step>

  <Step title="Ramp up gradually">
    Your reputation at your old provider doesn't come with you.
    [New-account sending limits](/deliverability/new-account-sending-limits) apply by your
    organization's age, and a sudden large send can be held for review. Send to your most engaged
    contacts first, then widen the audience over the following weeks.
  </Step>
</Steps>

When you've finished, revoke the API key you used at your old provider. Brudcast has already erased
its copy, but removing unused keys is good practice. Keep your old account until every domain is
verified here and your mail is flowing.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The provider rejected these credentials" icon="circle-alert">
    **Why:** the key is wrong, expired or lacks read access, or (for Mailgun and SparkPost) you chose
    the wrong region. **Fix:** create a fresh key with read access and check the region. See
    [Supported providers](#supported-providers).
  </Accordion>

  <Accordion title="“This connection has expired. Connect to the provider again to run a migration.”" icon="circle-alert">
    **Why:** the stored key was erased, because the run finished or was cancelled. **Fix:** start a
    new migration and connect again.
  </Accordion>

  <Accordion title="“This migration has already finished. Connect to the provider again to run another.”" icon="circle-alert">
    **Why:** a finished run can't be started again. **Fix:** start a new migration. Contacts you
    already imported are matched, so they aren't duplicated.
  </Accordion>

  <Accordion title="The error report says “Contact limit reached on your plan”" icon="circle-alert">
    **Why:** you reached your plan's contact allowance during the run. **Fix:** upgrade your plan,
    then run the migration again with **Leave them untouched** to bring in the rest.
  </Accordion>

  <Accordion title="A kind shows Failed" icon="circle-alert">
    **Why:** the provider refused that part of the request, often because the key lacks read access
    to it. The other kinds still ran. **Fix:** give the key the missing access and run a new
    migration for just that kind.
  </Accordion>
</AccordionGroup>

## Related

<Columns cols={2}>
  <Card title="Verify your domain" icon="shield-check" href="/channels/email/verify-your-domain">
    Publish DNS for the domains you brought across.
  </Card>

  <Card title="Placeholders and merge tags" icon="braces" href="/templates/placeholders-and-merge-tags">
    How template tags work once they're here.
  </Card>

  <Card title="Import contacts from a CSV" icon="download" href="/contacts/importing">
    For providers not on the list.
  </Card>
</Columns>
