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

# Create migration

> Queues a store-to-store migration and returns its `requestId`.

You reference stores by **BigCommerce store hash** — never by token. Vortex IQ resolves each store's API credentials server-side from your organisation's connected StagingPro integrations, so tokens never travel through the browser or your code.

Pass a `selection` to migrate a subset rather than the whole store.

**Guards enforced here**
- Source and destination must be different stores.
- `conflict_strategy: replace` ("Make Exact Copy") is refused when the destination is a live production store, because it deletes destination-only items.
- Selecting `orders`, `customers`, or `gift_certificates` requires `anonymise_customer_data: true`.
- Every `theme_activation` key must appear in `selection.themes`, and a destination channel may be targeted by at most one theme activation.
- Every `selection.page_template_globals` entry must appear in `selection.page_templates`.

<Note>Self-healing is always on. `auto_heal` is accepted for compatibility but the platform always runs migrations with auto-heal enabled — it and the shared learning layer materially reduce failed migrations, so it is not operator-optional.</Note>



## OpenAPI

````yaml vortex-apps/staging-pro/api/openapi.json POST /migrations
openapi: 3.1.0
info:
  title: StagingPro V2 Migration API (BigCommerce)
  version: 2.0.0
  description: >-
    REST API for the StagingPro V2 BC Migration app. Browse a connected store's
    catalogue, size a migration before you run it, launch bulk or selective
    store-to-store migrations, control runs, and follow live progress.


    All endpoints are served from `https://app.vortexiq.ai/v2/api/bc-migration`
    and are scoped to your organisation. Authenticate with a bearer token from
    the Login endpoint (scripts and services) or your signed-in session
    (browser). See
    [Authentication](/vortex-apps/staging-pro/api/authentication).
  contact:
    name: Vortex IQ Support
    url: https://www.vortexiq.ai/contact-us
servers:
  - url: https://app.vortexiq.ai/v2/api/bc-migration
    description: Vortex IQ platform (production)
security:
  - sessionAuth: []
  - bearerAuth: []
tags:
  - name: Authentication
    description: >-
      Exchange your identity and your organisation's API credential for a bearer
      token.
  - name: Migrations
    description: >-
      Create migrations, list history, read run detail and per-item issues, and
      follow live progress.
  - name: Migration Controls
    description: Pause, resume, cancel, and verify-only re-runs.
  - name: Counts & Forecast
    description: Size a migration before launching it. Neither endpoint creates a run.
paths:
  /migrations:
    post:
      tags:
        - Migrations
      summary: Create migration
      description: >-
        Queues a store-to-store migration and returns its `requestId`.


        You reference stores by **BigCommerce store hash** — never by token.
        Vortex IQ resolves each store's API credentials server-side from your
        organisation's connected StagingPro integrations, so tokens never travel
        through the browser or your code.


        Pass a `selection` to migrate a subset rather than the whole store.


        **Guards enforced here**

        - Source and destination must be different stores.

        - `conflict_strategy: replace` ("Make Exact Copy") is refused when the
        destination is a live production store, because it deletes
        destination-only items.

        - Selecting `orders`, `customers`, or `gift_certificates` requires
        `anonymise_customer_data: true`.

        - Every `theme_activation` key must appear in `selection.themes`, and a
        destination channel may be targeted by at most one theme activation.

        - Every `selection.page_template_globals` entry must appear in
        `selection.page_templates`.


        <Note>Self-healing is always on. `auto_heal` is accepted for
        compatibility but the platform always runs migrations with auto-heal
        enabled — it and the shared learning layer materially reduce failed
        migrations, so it is not operator-optional.</Note>
      operationId: createMigration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMigrationRequest'
            examples:
              bulk:
                summary: Bulk migration (whole catalogue)
                value:
                  source_store_hash: abc123xyz
                  destination_store_hash: def456uvw
                  selected_entities:
                    - products
                    - categories
                    - brands
                  mode: full
                  conflict_strategy: skip
              selective:
                summary: Selective migration (picked ids, themes, page templates)
                value:
                  source_store_hash: abc123xyz
                  destination_store_hash: def456uvw
                  selected_entities:
                    - products
                    - themes
                    - page_templates
                  selection:
                    products:
                      - 101
                      - 102
                      - 103
                    themes:
                      - 3f2b8c14-9d5e-4a71-b0c6-2e8f7a1d4b93
                    page_templates:
                      - pages/home
                      - pages/category
                    page_template_globals:
                      - pages/home
                  theme_activation:
                    3f2b8c14-9d5e-4a71-b0c6-2e8f7a1d4b93:
                      - 1
                  conflict_strategy: update
              singleStorefront:
                summary: One storefront to another (MSF)
                value:
                  source_store_hash: abc123xyz
                  destination_store_hash: def456uvw
                  selected_entities:
                    - products
                    - categories
                  source_channel_id: 1
                  destination_channel_id: 2
                  channel_scoped_products: true
                  source_storefront: UK Storefront
                  destination_storefront: UK Staging
              dryRun:
                summary: Dry run (no writes)
                value:
                  source_store_hash: abc123xyz
                  destination_store_hash: def456uvw
                  selected_entities:
                    - products
                  mode: dry-run
      responses:
        '201':
          description: Migration queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateMigrationResponse'
              example:
                requestId: 5A3C1B2E-7F4D-4E6A-9B8C-0D1E2F3A4B5C
                status: queued
                mode: full
                forecast:
                  products: 1200
                  variants: 5400
                  brands: 40
                  categories: 210
                  memMB: 2048
                  cpus: 2
                  etaSeconds: 5400
                  tier: medium
                  basis: forecast
                  at: '2026-07-16T09:00:00.000Z'
        '400':
          description: >-
            The migration service rejected the request (for example
            `anonymise_required`, or source and destination are the same store).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: >-
            Validation failed, a store is not found or not authorised for your
            organisation (`store_not_found`), or `replace` was requested against
            a production destination (`replace_not_allowed_on_production`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: replace_not_allowed_on_production
                message: >-
                  'Make Exact Copy' can't be used here: the destination you
                  selected is a live production store, and this option deletes
                  anything that exists only on the destination. Please choose
                  'Add New Only' or 'Update Existing', or pick a staging
                  (non-production) destination store.
        '502':
          $ref: '#/components/responses/ProxyFailed'
components:
  schemas:
    CreateMigrationRequest:
      type: object
      required:
        - source_store_hash
        - destination_store_hash
        - selected_entities
      properties:
        source_store_hash:
          type: string
          description: >-
            BigCommerce store hash to migrate **from**. Must be connected to
            your organisation.
        destination_store_hash:
          type: string
          description: >-
            BigCommerce store hash to migrate **to**. Must differ from the
            source.
        selected_entities:
          type: array
          items:
            type: string
          minItems: 1
          description: >-
            Which entity groups to migrate, for example `products`,
            `categories`, `brands`, `pages`, `promotions`, `themes`,
            `page_templates`, `orders`, `customers`.
        mode:
          type: string
          enum:
            - full
            - dry-run
            - verify
            - counts
          default: full
          description: >-
            `full` migrates; `dry-run` walks the migration without writing;
            `verify` compares only; `counts` totals only.
        anonymise_customer_data:
          type: boolean
          default: false
          description: >-
            Required `true` when `orders`, `customers`, or `gift_certificates`
            are selected.
        conflict_strategy:
          type: string
          enum:
            - skip
            - update
            - replace
          description: >-
            How an entity that already exists on the destination is handled:

            - `skip` — Add New Only (default): leave existing items untouched.

            - `update` — Update Existing: additively update changed fields.

            - `replace` — Make Exact Copy: update, then delete destination-only
            items. **Refused against a production destination.**
        activate_theme:
          type: boolean
          description: >-
            Bulk theme migration: activate the migrated theme on the destination
            rather than only uploading it. Ignored when `selection.themes` is
            present — use `theme_activation` instead.
        auto_heal:
          type: boolean
          description: Accepted for compatibility only. Self-healing is always enabled.
        source_channel_id:
          type: integer
          minimum: 1
          description: >-
            Multi-storefront: the source storefront to migrate from. Pair with
            `destination_channel_id`.
        destination_channel_id:
          type: integer
          minimum: 1
          description: 'Multi-storefront: the destination storefront to map onto.'
        channel_scoped_products:
          type: boolean
          description: >-
            Migrate only products assigned to `source_channel_id`. When false,
            all products migrate and are assigned to the mapped destination
            channel.
        channel_map:
          type: object
          additionalProperties:
            type: integer
          description: >-
            Explicit source channel id → destination channel id map, for mapping
            several storefronts at once.
        source_storefront:
          type: string
          description: Source storefront name, recorded for display in History.
        destination_storefront:
          type: string
          description: Destination storefront name, recorded for display in History.
        selection:
          $ref: '#/components/schemas/Selection'
        theme_activation:
          $ref: '#/components/schemas/ThemeActivation'
    CreateMigrationResponse:
      type: object
      properties:
        requestId:
          type: string
          description: Identifies the run. Use it with every other migration endpoint.
        status:
          type: string
          enum:
            - queued
        mode:
          type: string
          enum:
            - full
            - dry-run
            - verify
            - counts
        forecast:
          oneOf:
            - $ref: '#/components/schemas/Forecast'
            - type: 'null'
          description: Sizing forecast computed at kickoff, when available.
    Error:
      type: object
      properties:
        error:
          type: string
          description: Machine-readable error code.
        message:
          type: string
          description: Human-readable explanation.
        status:
          type: integer
          description: 'Present on `bc_error`: the status BigCommerce returned.'
        errors:
          type: object
          additionalProperties: true
          description: 'Present on validation failures: field-level messages.'
    Selection:
      type: object
      description: >-
        Selective migration picks. Parents and ancestors are included
        automatically, and products pull in their brands and categories.


        Find the ids to pass here in the StagingPro app's Selective Migration
        tab. A selection also scopes [entity
        counts](/vortex-apps/staging-pro/api/get-entity-counts) and the
        [forecast](/vortex-apps/staging-pro/api/get-forecast).
      properties:
        products:
          type: array
          items:
            type: integer
          maxItems: 5000
          description: Product ids.
        categories:
          type: array
          items:
            type: integer
          maxItems: 5000
          description: Category ids.
        pages:
          type: array
          items:
            type: integer
          maxItems: 2000
          description: Page ids.
        promotions:
          type: array
          items:
            type: integer
          maxItems: 2000
          description: Promotion ids.
        themes:
          type: array
          items:
            type: string
            format: uuid
          maxItems: 20
          description: >-
            Theme **UUIDs** (BigCommerce theme ids are uuids, not integers).
            When present, `activate_theme` is ignored in favour of
            `theme_activation`.
        page_templates:
          type: array
          items:
            type: string
            pattern: ^pages/
          maxItems: 50
          description: >-
            Page template files, all beginning `pages/` (for example
            `pages/home`). Template-level Page Builder content only — content
            scoped to a specific product, category, or page is not included.
        page_template_globals:
          type: array
          items:
            type: string
            pattern: ^pages/
          maxItems: 50
          description: >-
            Page templates whose **global** widget content also migrates. Must
            be a subset of `page_templates`.
    ThemeActivation:
      type: object
      additionalProperties:
        type: array
        items:
          type: integer
          minimum: 1
        minItems: 1
      description: >-
        Selective themes only: which destination channels to **activate** each
        picked theme on, keyed by source theme uuid. Omit to upload the themes
        without activating them.


        Every key must appear in `selection.themes`, and a destination channel
        may be targeted by at most one theme — a storefront channel has exactly
        one active theme.
      example:
        3f2b8c14-9d5e-4a71-b0c6-2e8f7a1d4b93:
          - 1
          - 2
    Forecast:
      type: object
      description: >-
        Sizing and ETA estimate.


        Sizing and ETA are priced from different bases deliberately: the ETA
        follows the run's real scope (picks, strategy, order statuses), while
        memory is pinned to **store scale** — the engine's working set does not
        shrink with a small pick, so sizing on picked counts alone would
        under-provision the run.
      properties:
        products:
          type: integer
        variants:
          type: integer
        brands:
          type: integer
        categories:
          type: integer
        counts:
          type: object
          additionalProperties:
            type: integer
          description: >-
            Per-entity counts the estimate was priced from, scoped by
            `selection` when supplied.
        estimated:
          type: array
          items:
            type: string
          description: Count keys that are sampled estimates rather than exact totals.
        memMB:
          type: integer
          description: >-
            Memory the run will be given (MiB). Pinned to store scale, not to
            the picked subset.
        cpus:
          type: number
          description: CPUs the run will be given.
        etaSeconds:
          type: integer
          description: Estimated total wall-clock for the run.
        etaBreakdown:
          type: object
          additionalProperties:
            type: number
          description: Per-phase seconds.
        etaAssumedRttMs:
          type: number
          description: The blended latency the estimate assumed.
        etaAssumedMs:
          type: object
          description: >-
            Per-operation-class latencies the estimate assumed, so heavy writes
            are not judged against a blended baseline.
          properties:
            read:
              type: number
            write:
              type: number
            heavy:
              type: number
        strategy:
          type: string
          enum:
            - skip
            - update
            - replace
          description: >-
            The conflict strategy the ETA was priced with. Absent means
            create-only.
        destProducts:
          type: integer
          description: >-
            Destination catalogue size at kickoff. Absent or `0` when the
            destination wasn't known.
        tier:
          type: string
          enum:
            - small
            - medium
            - large
            - xlarge
            - xxlarge
            - xxxlarge
          description: Capacity band the run was placed in.
        basis:
          type: string
          enum:
            - forecast
            - fallback
            - fix
          description: '`fallback` means counts were unavailable and defaults were used.'
        at:
          type: string
          format: date-time
  responses:
    Forbidden:
      description: >-
        Store Migration is not enabled for your organisation. Requests with no
        valid bearer token or session return `401` instead.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Store Migration is not enabled for this organization.
    ProxyFailed:
      description: The migration service is unreachable (`proxy_failed`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: proxy_failed
            message: migration service unreachable
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: cookie
      name: vortexiq_session
      description: >-
        Your signed-in Vortex IQ session (browser calls). Requests are scoped to
        your organisation, which must have Store Migration enabled. For
        server-to-server calls use a bearer token instead — see
        [Authentication](/vortex-apps/staging-pro/api/authentication).
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: >-
        A bearer token from [Login](/vortex-apps/staging-pro/api/login). Valid
        for 10 days; scoped to the user and organisation it was issued for.
        Rotating or disabling the organisation's API credential immediately
        invalidates all outstanding tokens.

````