# Contentstack CLI and content migration

### About this export

| Field | Value |
| --- | --- |
| **content_type** | lesson |
| **platform** | contentstack-academy |
| **source_url** | https://www.contentstack.com/academy/courses/apis-and-developer-tooling/contentstack-cli-and-content-migration |
| **course_slug** | apis-and-developer-tooling |
| **lesson_slug** | contentstack-cli-and-content-migration |
| **markdown_file_url** | /academy/md/courses/apis-and-developer-tooling/contentstack-cli-and-content-migration.md |
| **generated_at** | 2026-08-03T11:49:31.114Z |

> Part of **[APIs and Developer Tooling](https://www.contentstack.com/academy/courses/apis-and-developer-tooling)** on Contentstack Academy. **Academy MD v3** — structured for retrieval; no quiz or assessment keys.

<!-- ai_metadata: {"lesson_id":"15","type":"text","duration_minutes":1,"topics":["Contentstack","CLI","and","content","migration"]} -->

#### Lesson text

# Contentstack CLI and content migration

> **TL;DR**
> 
> *   Use csdx cm:stacks:export and csdx cm:stacks:import to replicate content model changes across stacks in a scriptable, auditable way.
> *   Always export assets alongside entries to avoid broken references in the target stack, and back up production before importing with \--replace-existing.
> *   Wrap CLI export/import workflows in CI/CD pipelines for consistent, automated content model synchronization across a multi-brand portfolio.

Managing a multi-brand publishing platform through the Contentstack web interface works until it does not. When you operate three brands with separate stacks, each with their own content types, entries, and assets, clicking through the UI to replicate a content model change across all three stacks is slow, error-prone, and impossible to audit. The Contentstack CLI (csdx) turns these operations into repeatable, scriptable commands that belong in your development workflow alongside your code.

This lesson is hands-on. By the end, you should be able to install the CLI, authenticate, export content from one stack, import it into another, and understand how these operations fit into automated pipelines for a multi-brand content operation.

## Installing the Contentstack CLI

The CLI is distributed as an npm package. Install it globally:

npm install -g @contentstack/cli

After installation, verify it works:

csdx --version

The CLI uses a plugin architecture. Core commands cover stack management, content export/import, and authentication. Additional plugins extend functionality for specific use cases. For a multi-brand publishing platform, the core commands handle the majority of migration work.

## Authenticating with the CLI

Before running any stack operations, you need to authenticate. The CLI supports multiple authentication methods:

### Interactive login

csdx auth:login

This opens a browser-based OAuth flow. After authenticating, the CLI stores your session credentials locally. This method works well for local development but is not suitable for CI/CD pipelines.

### Token-based authentication

For automated workflows and CI/CD, add the management token as an alias, then reference that alias in commands:

csdx auth:tokens:add \\
  --alias "source-stack" \\
  --stack-api-key "blt\_matrix\_link\_001" \\
  --management \\
  --token "cs1234567890abcdef" \\
  --yes

csdx cm:stacks:export \\
  --alias "source-stack" \\
  --data-dir ./export-data

Management tokens are created in the Contentstack dashboard under Settings > Tokens. Each token can be scoped to specific permissions, which matters when running migrations across a multi-brand portfolio where different teams own different stacks.

### Listing and managing tokens

\# List stored tokens
csdx auth:tokens

# Add a named token for a specific stack
csdx auth:tokens:add \\
  --alias "brand-alpha-dev" \\
  --stack-api-key "blt\_alpha\_key" \\
  --management --token "cs\_alpha\_mgmt\_token" \\
  --yes

# Remove a stored token
csdx auth:tokens:remove --alias "brand-alpha-dev"

Token aliases simplify repeated operations. For a publishing platform managing Brand Alpha, Brand Beta, and Brand Gamma, you might configure:

csdx auth:tokens:add --alias "alpha-prod" --stack-api-key "$NEXT\_PUBLIC\_CONTENTSTACK\_ALPHA\_API\_KEY" --management --token "$NEXT\_PUBLIC\_CONTENTSTACK\_ALPHA\_MANAGEMENT\_TOKEN" --yes
csdx auth:tokens:add --alias "beta-prod" --stack-api-key "$NEXT\_PUBLIC\_CONTENTSTACK\_BETA\_API\_KEY" --management --token "$NEXT\_PUBLIC\_CONTENTSTACK\_BETA\_MANAGEMENT\_TOKEN" --yes
csdx auth:tokens:add --alias "gamma-prod" --stack-api-key "$NEXT\_PUBLIC\_CONTENTSTACK\_GAMMA\_API\_KEY" --management --token "$NEXT\_PUBLIC\_CONTENTSTACK\_GAMMA\_MANAGEMENT\_TOKEN" --yes

Now every subsequent command can reference \--alias alpha-prod instead of passing raw keys.

## Key CLI commands for content operations

### Seeding a new stack

When launching a new brand in the publishing platform, you often want to start from a known content model rather than building from scratch. The seed command creates a new stack from an existing template:

csdx cm:stacks:seed \\
  --repo "contentstack/stack-starter-app" \\
  --stack-name "Brand Delta - Development"

This clones a predefined content model into a fresh stack. For a multi-brand platform, you might maintain a custom seed repository that contains your standardized product, product line, category, page, and header content types - the shared foundation that every brand starts from. The [Veda kickstart seed](https://github.com/contentstack/kickstart-veda-seed) is an example.

### Exporting content

The export command extracts content from a stack into a local directory structure:

\# Full stack export
csdx cm:stacks:export \\
  --alias "alpha-prod" \\
  --data-dir ./exports/alpha

# Export specific modules only
csdx cm:stacks:export \\
  --alias "alpha-prod" \\
  --module content-types \\
  --data-dir ./exports/alpha-content-types

# Export multiple specific modules
csdx cm:stacks:export \\
  --alias "alpha-prod" \\
  --module content-types \\
  --module global-fields \\
  --module assets \\
  --data-dir ./exports/alpha-schema

The exported directory contains JSON files organized by module:

exports/alpha/
  content-types/
    product.json
    product\_line.json
    category.json
    header.json
  entries/
    product/
      en-us/
        blt\_entry\_1.json
        blt\_entry\_2.json
    product\_line/
      en-us/
        blt\_entry\_3.json
  assets/
    blt\_asset\_1.json
    blt\_asset\_2.json
  global-fields/
    seo\_metadata.json
  environments/
    development.json
    production.json

This file structure is both human-readable and machine-processable. You can inspect it, commit it to version control, modify individual files, and import the result into another stack.

### Importing content

The import command pushes exported content into a target stack:

\# Full import into a different stack
csdx cm:stacks:import \\
  --alias "beta-prod" \\
  --data-dir ./exports/alpha

# Import specific modules only
csdx cm:stacks:import \\
  --alias "beta-prod" \\
  --module content-types \\
  --data-dir ./exports/alpha-content-types

Import respects dependencies: global fields are created before content types that reference them, and content types are created before entries that use them. However, the CLI cannot resolve every dependency automatically, particularly when entries reference other entries across content types that may not exist in the target stack.

## Migrating content between stacks

The primary migration use case for a multi-brand publishing platform is replicating content model changes across brand stacks. When the platform team adds a new seo\_metadata global field to the shared product model, that change needs to propagate to every brand stack.

### Step-by-step migration workflow

1.  Make the change in the source stack (e.g., Add the social\_links global field to Brand Alpha's development stack).
2.  Export the changed modules.
    
    csdx cm:stacks:export \\
      --alias "alpha-dev" \\
      --module content-types \\
      --module global-fields \\
      --data-dir ./migration/social-links-update
    
3.  Review the export: inspect the JSON files to confirm only the intended changes are present. Remove any content type files that were not modified to avoid overwriting unchanged types in the target.
4.  Import into target stacks.
    
    \# Apply to Brand Beta
    csdx cm:stacks:import \\
      --alias "beta-dev" \\
      --module content-types \\
      --module global-fields \\
      --data-dir ./migration/social-links-update
    
    # Apply to Brand Gamma
    csdx cm:stacks:import \\
      --alias "gamma-dev" \\
      --module content-types \\
      --module global-fields \\
      --data-dir ./migration/social-links-update
    
5.  Verify in each target stack: confirm the global field and content type changes appear correctly in the Contentstack dashboard for each brand.
6.  Promote through environments: once verified in development stacks, repeat the import against staging and production stacks.

### Handling conflicts

Import operations can encounter conflicts when the target stack already has a content type with the same UID but a different structure. The CLI provides flags to control behavior:

\# Overwrite existing content types during import
csdx cm:stacks:import \\
  --alias "beta-dev" \\
  --module content-types \\
  --data-dir ./migration/social-links-update \\
  --replace-existing

Without \--replace-existing, the CLI skips content types that already exist. For schema migrations, you typically want to overwrite. For entry imports, the decision depends on whether you want to merge or replace.

## Programmatic migrations using the CMA

For migrations that go beyond simple export/import - renaming fields, transforming data, backfilling values - you need programmatic scripts that use the Content Management API directly.

Consider a scenario where the Veda platform needs to migrate all product entries to include a new description\_word\_count field, calculated from the description text:

// scripts/backfill-description-word-count.ts
// Veda: backfill description\_word\_count across all products

import contentstack from "@contentstack/management";

const client = contentstack.client({
  host: "https://api.contentstack.io",
});

async function backfillDescriptionWordCount(stackApiKey: string, managementToken: string) {
  const stack = client.stack({
    api\_key: stackApiKey,
    management\_token: managementToken,
  });

  // Fetch all product entries
  const products = await stack
    .contentType("product")
    .entry()
    .query({ include\_count: true })
    .find();

  console.log(\`Processing ${products.items.length} products...\`);

  for (const product of products.items) {
    const descText = product.description || product.short\_description || "";
    const wordCount = descText.split(/\\s+/).filter(Boolean).length;

    product.description\_word\_count = wordCount;

    try {
      await product.update();
      console.log(\`Updated "${product.title}" → ${wordCount} words\`);
    } catch (err: unknown) {
      console.error(\`Failed to update "${product.title}": ${(err as Error).message}\`);
    }
  }
}

// Run across all brand stacks
async function main() {
  const brands = \[
    { name: "Alpha", apiKey: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_ALPHA\_API\_KEY!, token: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_ALPHA\_MANAGEMENT\_TOKEN! },
    { name: "Beta", apiKey: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_BETA\_API\_KEY!, token: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_BETA\_MANAGEMENT\_TOKEN! },
    { name: "Gamma", apiKey: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_GAMMA\_API\_KEY!, token: process.env.NEXT\_PUBLIC\_CONTENTSTACK\_GAMMA\_MANAGEMENT\_TOKEN! },
  \];

  for (const brand of brands) {
    console.log(\`\\n--- Backfilling ${brand.name} ---\`);
    await backfillDescriptionWordCount(brand.apiKey, brand.token);
  }
}

main().catch(console.error);

Run the script with:

npx tsx scripts/backfill-reading-time.ts

Programmatic migrations give you full control over transformation logic, error handling, and execution order. They complement CLI export/import for cases where raw JSON file manipulation is insufficient.

## Using the CLI in CI/CD pipelines

For a multi-brand publishing platform, content model consistency across stacks should not depend on manual CLI runs. Automate it.

\# .github/workflows/content-model-sync.yml
name: Sync Content Model Across Brands
on:
  workflow\_dispatch:
    inputs:
      source\_alias:
        description: "Source stack alias (e.g., alpha-dev)"
        required: true
      modules:
        description: "Modules to sync (e.g., content-types,global-fields)"
        required: true
        default: "content-types,global-fields"

jobs:
  export:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Contentstack CLI
        run: npm install -g @contentstack/cli
      - name: Configure source token
        run: |
          csdx auth:tokens:add \\
            --alias source \\
            --stack-api-key ${{ secrets.SOURCE\_API\_KEY }} \\
            --management --token ${{ secrets.SOURCE\_MGMT\_TOKEN }} \\
            --yes
      - name: Export from source
        run: |
          IFS=',' read -ra MODULES <<< "${{ inputs.modules }}"
          MODULE\_FLAGS=""
          for mod in "${MODULES\[@\]}"; do
            MODULE\_FLAGS="$MODULE\_FLAGS --module $mod"
          done
          csdx cm:stacks:export --alias source $MODULE\_FLAGS --data-dir ./export-data
      - name: Upload export artifact
        uses: actions/upload-artifact@v4
        with:
          name: content-model-export
          path: ./export-data

  import:
    needs: export
    runs-on: ubuntu-latest
    strategy:
      matrix:
        brand: \[beta, gamma\]
    steps:
      - name: Install Contentstack CLI
        run: npm install -g @contentstack/cli
      - name: Download export artifact
        uses: actions/download-artifact@v4
        with:
          name: content-model-export
          path: ./export-data
      - name: Configure target token
        run: |
          csdx auth:tokens:add \\
            --alias target \\
            --stack-api-key ${{ secrets\[format('{0}\_API\_KEY', matrix.brand)\] }} \\
            --management --token ${{ secrets\[format('{0}\_MGMT\_TOKEN', matrix.brand)\] }} \\
            --yes
      - name: Import to target
        run: |
          IFS=',' read -ra MODULES <<< "${{ inputs.modules }}"
          MODULE\_FLAGS=""
          for mod in "${MODULES\[@\]}"; do
            MODULE\_FLAGS="$MODULE\_FLAGS --module $mod"
          done
          csdx cm:stacks:import --alias target $MODULE\_FLAGS --data-dir ./export-data --replace-existing

This workflows exports the content model from one stack and fans out imports across brand stacks in parallel. The workflow\_dispatch trigger lets the platform team run it on demand with specific parameters, and the matrix strategy scales to any number of brands.

## Exercise: export content types from one stack and import into another

Put the concepts together in a practical exercise.

**Prerequisites:** Two Contentstack stacks (or two environments within the same organization) with management tokens configured.

**Steps:**

1.  Install the CLI and authenticate:
    
    npm install -g @contentstack/cli
    csdx auth:tokens:add --alias "source" --stack-api-key "$SOURCE\_KEY" --management --token "$SOURCE\_TOKEN" --yes
    csdx auth:tokens:add --alias "target" --stack-api-key "$TARGET\_KEY" --management --token "$TARGET\_TOKEN" --yes
    
2.  Export content types and global fields from the source stack:
    
    csdx cm:stacks:export --alias "source" --module content-types --module global-fields --data-dir ./exercise-export
    
3.  Inspect the exported files:
    
    ls -la ./exercise-export/content-types/
    # You should see JSON files for each content type
    
4.  Import into the target stack:
    
    csdx cm:stacks:import --alias "target" --module content-types --module global-fields --data-dir ./exercise-export
    
5.  Open the target stack in Contentstack and verify the content types match the source.
6.  Modify a content type in the source stack (add a field), re-export, and re-import with \--replace-existing to practice incremental migration.

**Expected outcome:** The target stack contains identical content types to the source, including any modifications applied in step 6.

## Common mistakes

### Forgetting to export assets

Entries often reference assets (images, documents, videos). Exporting entries without their assets produces an import that contains broken asset references. When migrating entries across stacks, always include the assets module in your export, or confirm that referenced assets already exist in the target stack.

\# Correct: export entries WITH assets
csdx cm:stacks:export \\
  --alias "source" \\
  --module entries \\
  --module assets \\
  --data-dir ./full-migration

### Ignoring reference integrity

Entries reference other entries by UID. When you import entries into a stack that does not contain the referenced entries, those references break silently: the entry imports successfully, but the reference field points to a non-existent UID. Always import referenced content types and their entries before importing the entries that reference them. For Veda where products reference categories and product lines, import categories and product lines first, then products.

> **Common pitfall:**
> 
> Running \--replace-existing imports against a production stack without first exporting a backup leaves you with no rollback path if the migration produces unexpected results.

### Running migrations without a backup

Before running any import with \--replace-existing against a production stack, export the current state first. This gives you a reliable rollback path if the migration produces unexpected results:

\# Backup before migrating
csdx cm:stacks:export --alias "prod" --data-dir ./backup/$(date +%Y%m%d)

# Then run the migration
csdx cm:stacks:import --alias "prod" --data-dir ./migration-data --replace-existing

### Hardcoding stack credentials in scripts

Migration scripts that contain raw API keys and management tokens are security liabilities. Use environment variables or the CLI's token alias system. Never commit credentials to version control, even in "internal" repositories.

### Assuming export/import is atomic

The CLI processes modules sequentially and entries individually. A network interruption mid-import can leave the target stack in a partial state. For large migrations, consider breaking the operation into smaller batches and tracking progress with explicit logging to redirect success/failure statuses for post-migration verification:

csdx cm:stacks:import --alias "target" --data-dir ./migration-data 2>&1 | tee migration-log.txt

## Summary

The Contentstack CLI transforms content operations from manual, click-heavy processes into scriptable, auditable commands. For a multi-brand publishing platform, the CLI is essential: exporting content models from a reference stack, importing them across brand stacks, seeding new brand stacks from templates, and running programmatic migrations for data transformations that go beyond structural changes.

The export/import workflow follows a clear pattern: export modules from the source, inspect and optionally modify the JSON files, import into targets. Wrapping this pattern in CI/CD automation ensures consistency across a growing portfolio of brands without relying on manual coordination. The key operational discipline is treating migrations like database migrations in application development: plan them, test them against non-production stacks, back up before applying to production, and log everything.

#### Key takeaways

- Connect **Contentstack CLI and content migration** back to your stack configuration before moving to the next module.
- Capture one concrete artifact (screenshot, Postman call, or code snippet) that proves the step works in your environment.
- Re-read the delivery versus management boundary for anything you changed in the entry model.

## Supplement for indexing

### Content summary

Contentstack CLI and content migration. Contentstack CLI and content migration TL;DR Use csdx cm:stacks:export and csdx cm:stacks:import to replicate content model changes across stacks in a scriptable, auditable way. Always export assets alongside entries to avoid broken references in the target stack, and back up production before importing with \--replace-existing. Wrap CLI export/import workflows in CI/CD pipelines for consistent, automated content model synchronization across a multi-brand portfolio. Managing a multi-brand publishing platform through the Contentstack web interface works until it does not. When you operate three brands with separate stacks, each with their own content types, entries, and assets, clicking throu

### Retrieval tags

- Contentstack
- CLI
- and
- content
- migration
- apis-and-developer-tooling
- lesson 15
- Contentstack CLI and content migration
- apis-and-developer-tooling lesson

### Indexing notes

Index this lesson as a primary chunk tagged with lesson_id "15" and topics: [Contentstack, CLI, and, content, migration].
Parent course slug: apis-and-developer-tooling. Use asset_references URLs as thumbnail hints in search results when present.
Never surface LMS quiz content or assessment answers from this file.

### Asset references

_No image or video thumbnail URLs were extracted._

### External links

| Label | URL |
| --- | --- |
| Contentstack Academy home | `https://www.contentstack.com/academy/` |
| Training instance setup | `https://www.contentstack.com/academy/training-instance` |
| Academy playground (GitHub) | `https://github.com/contentstack/contentstack-academy-playground` |
| Contentstack documentation | `https://www.contentstack.com/docs/` |
| Veda kickstart seed | `https://github.com/contentstack/kickstart-veda-seed` |
