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

# Start an import

> Say what to bring and start. The order is fixed: the do-not-contact list first, so nothing that arrives later is queued before its opt-out is known; then the chosen campaigns, recreated as **draft** campaigns with their email steps; then their leads, with custom variables as lead fields.

Nothing is sent. The campaigns stay drafts with no inbox until you launch them in ManyPI. With `enrollNotContacted`, leads the old tool never emailed are queued in their new campaign, so the sequence carries on once it is launched. Leads that replied, bounced or unsubscribed are never queued.

The first slice runs inside this request, so the response already shows progress. After that the import runs in slices: call `POST /api/integrations/migrations/{id}/advance` until `status` is `done`. An import nobody advances is picked up by ManyPI's scheduler, a few seconds every five minutes, so it finishes either way.

Only campaign ids the scan found are used; others are dropped. Calling this on an import that is not `scanned` returns it unchanged.

At most 100,000 leads per import, and the plan's lead allowance applies (leads over it are counted in `overLimit`). Custom fields need the Starter plan and queuing leads needs a paid plan; without them the import still runs and says what stayed behind in `warnings`.

Requires the `write` permission.



## OpenAPI

````yaml POST /api/integrations/migrations/{id}
openapi: 3.1.0
info:
  title: ManyPI API
  description: >-
    The REST API behind ManyPI — AI lead generation, email validation,
    cold-email outreach, agent runs and web scraping. Every endpoint is
    authenticated with a ManyPI API key (`mpi_...`) or an OAuth 2.1 access
    token, and honours the permissions granted to that credential.
  version: 2.0.0
  contact:
    name: ManyPI Support
    email: support@manypi.com
    url: https://manypi.com
servers:
  - url: https://app.manypi.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Leads
    description: Find, store, enrich and manage leads.
  - name: Email validation
    description: Verify that lead addresses can receive mail.
  - name: Outreach
    description: Inboxes, sequences, campaigns, sending and replies.
  - name: Agent
    description: Agent runs, replies and skills.
  - name: Scraping
    description: Scrapers, runs and extracted data.
  - name: Endpoints
    description: Publish scrapers as typed REST endpoints.
  - name: Account
    description: Key validation and account information.
  - name: Imports
    description: >-
      Bring campaigns, leads and the do-not-contact list over from another
      outreach tool.
paths:
  /api/integrations/migrations/{id}:
    post:
      tags:
        - Imports
      summary: Start an import
      description: >-
        Say what to bring and start. The order is fixed: the do-not-contact list
        first, so nothing that arrives later is queued before its opt-out is
        known; then the chosen campaigns, recreated as **draft** campaigns with
        their email steps; then their leads, with custom variables as lead
        fields.


        Nothing is sent. The campaigns stay drafts with no inbox until you
        launch them in ManyPI. With `enrollNotContacted`, leads the old tool
        never emailed are queued in their new campaign, so the sequence carries
        on once it is launched. Leads that replied, bounced or unsubscribed are
        never queued.


        The first slice runs inside this request, so the response already shows
        progress. After that the import runs in slices: call `POST
        /api/integrations/migrations/{id}/advance` until `status` is `done`. An
        import nobody advances is picked up by ManyPI's scheduler, a few seconds
        every five minutes, so it finishes either way.


        Only campaign ids the scan found are used; others are dropped. Calling
        this on an import that is not `scanned` returns it unchanged.


        At most 100,000 leads per import, and the plan's lead allowance applies
        (leads over it are counted in `overLimit`). Custom fields need the
        Starter plan and queuing leads needs a paid plan; without them the
        import still runs and says what stayed behind in `warnings`.


        Requires the `write` permission.
      operationId: startImport
      parameters:
        - name: id
          in: path
          required: true
          description: The import id, from `POST /api/integrations/migrations`.
          schema:
            type: string
            format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                options:
                  $ref: '#/components/schemas/MigrationOptions'
            example:
              options:
                campaigns:
                  - c1f0a2b3-instantly-campaign-id
                recreateCampaigns: true
                importLeads: true
                doNotContact: true
                enrollNotContacted: true
                duplicates: merge
      responses:
        '200':
          description: The import, now `running` (or already `done` for a small account).
          content:
            application/json:
              schema:
                type: object
                properties:
                  job:
                    $ref: '#/components/schemas/MigrationJob'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: The workspace is locked for billing (past due or ended plan).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    MigrationOptions:
      type: object
      properties:
        campaigns:
          type: array
          items:
            type: string
          maxItems: 500
          default: []
          description: >-
            Campaign ids from `scan.campaigns`. Leads are read through these
            campaigns, so for lemlist and Smartlead a lead in no chosen campaign
            stays behind; Instantly also brings leads that sit in lists, and
            Apollo, Snov.io and Saleshandy bring every contact.
        recreateCampaigns:
          type: boolean
          default: true
          description: Recreate the chosen campaigns as drafts with their email steps.
        importLeads:
          type: boolean
          default: true
        doNotContact:
          type: boolean
          default: true
          description: Bring the block list, plus leads that bounced or unsubscribed there.
        enrollNotContacted:
          type: boolean
          default: true
          description: >-
            Queue leads the old tool never emailed into their new draft
            campaign. Needs `recreateCampaigns` and a paid plan.
        duplicates:
          type: string
          enum:
            - merge
            - overwrite
            - skip
          default: merge
          description: >-
            A lead that already exists: `merge` fills in only its empty fields,
            `overwrite` replaces its fields, `skip` leaves it alone.
        asCustomers:
          type: boolean
          default: false
          description: >-
            Contact imports: also put every imported address on do-not-contact
            (existing customers).
    MigrationJob:
      type: object
      properties:
        id:
          type: string
          format: uuid
        provider:
          type: string
          description: The tool imported from, e.g. `instantly` or `hubspot`.
        account_label:
          type: string
          nullable: true
          description: The account as the other tool names it.
        status:
          type: string
          enum:
            - scanned
            - running
            - done
            - failed
            - cancelled
        scan:
          type: object
          description: What the scan found.
          properties:
            label:
              type: string
            campaigns:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  status:
                    type: string
                    description: The other tool's own word for it.
                  emailSteps:
                    type: integer
                    nullable: true
                  leads:
                    type: integer
                    nullable: true
            leadsTotal:
              type: integer
              nullable: true
              description: Null when the tool cannot count cheaply.
            blocklistTotal:
              type: integer
              nullable: true
            accounts:
              type: array
              description: Sending mailboxes in the other tool. They are listed, not moved.
              items:
                type: object
                properties:
                  email:
                    type: string
                  provider:
                    type: string
                    enum:
                      - google
                      - microsoft
                      - smtp
                      - other
                  name:
                    type: string
                    nullable: true
            notes:
              type: array
              items:
                type: string
              description: >-
                Worth knowing before starting, e.g. a key that cannot read
                sequences.
        options:
          $ref: '#/components/schemas/MigrationOptions'
        progress:
          type: object
          nullable: true
          properties:
            phase:
              type: string
              enum:
                - blocklist
                - campaigns
                - leads
                - done
            campaignIndex:
              type: integer
            campaignMap:
              type: object
              additionalProperties:
                type: string
              description: Other tool's campaign id to ManyPI campaign id.
            counts:
              type: object
              properties:
                suppressed:
                  type: integer
                  description: Addresses and domains added to do-not-contact.
                campaigns:
                  type: integer
                  description: Draft campaigns created.
                steps:
                  type: integer
                  description: Email steps recreated across them.
                created:
                  type: integer
                  description: New leads.
                updated:
                  type: integer
                  description: Existing leads merged into.
                skipped:
                  type: integer
                  description: >-
                    Rows not written: duplicates, rows with no email or other
                    identity, and conflicts.
                overLimit:
                  type: integer
                  description: Leads left out because the plan's lead allowance was full.
                failed:
                  type: integer
                enrolled:
                  type: integer
                  description: Leads queued in their new draft campaign.
                customFields:
                  type: integer
                  description: Custom variables created as lead fields.
            warnings:
              type: array
              items:
                type: string
            skippedSteps:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
              description: >-
                Per ManyPI campaign, steps that could not come over, e.g. `2
                LinkedIn steps`.
            leadsSeen:
              type: integer
        result:
          type: object
          nullable: true
          description: >-
            Set when the import ends: `counts`, `campaignMap`, `warnings` and
            `skippedSteps`, as in `progress`.
        error:
          type: string
          nullable: true
          description: Why a `failed` import stopped. What came over before it is kept.
        created_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
          nullable: true
        finished_at:
          type: string
          format: date-time
          nullable: true
    Error:
      type: object
      properties:
        error:
          description: >-
            Human-readable message. A few validation failures (creating or
            updating an endpoint, bulk lead updates and deletes) send a list of
            the individual problems here instead of one string.
          oneOf:
            - type: string
            - type: array
              items:
                type: object
                properties:
                  path:
                    type: array
                    items:
                      type:
                        - string
                        - integer
                  message:
                    type: string
        code:
          type: string
          description: >-
            A machine-readable code on gated failures: `lead_limit`,
            `outreach_locked`, `validation_credits_exhausted`.
        details:
          description: >-
            Extra detail on some failures, such as the list of parameters that
            failed an endpoint's `param_schema`.
  responses:
    BadRequest:
      description: The request body or parameters failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: >-
        Missing, invalid or expired credential — or a key that lacks the
        permission this route requires.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: No such resource in this workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        A ManyPI API key (`mpi_...`) from the profile menu → API Access in the
        dashboard, or an OAuth 2.1 access token obtained through the MCP consent
        flow.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.