The CLI Bulk Operations plugin streamlines content management workflows by providing robust commands for bulk publishing and unpublishing operations. It intelligently handles API rate limits, retries failed operations, and processes large volumes of content efficiently.
When managing content at scale in Contentstack, you often need to:
This plugin handles all of these challenges automatically.
# Install Contentstack CLI
npm install -g @contentstack/cli
# Verify installation
csdx cm:stacks:bulk-entries --help
# Publish all entries of a content type
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--environments production \
--locales en-us \
--stack-api-key blt*******
# Publish assets
csdx cm:stacks:bulk-assets \
--operation publish \
--environments production \
--locales en-us \
--stack-api-key blt*******
# Unpublish entries
csdx cm:stacks:bulk-entries \
--operation unpublish \
--content-types blog_post \
--environments staging \
--locales en-us \
--stack-api-key blt*******
Perform bulk operations on entries with advanced filtering and publishing options.
csdx cm:stacks:bulk-entries [OPTIONS]
| Flag | Description | Example |
|---|---|---|
| --operation | Operation type: publish or unpublish | --operation publish |
| --environments | The name of the environment(s) on which entries/assets will be published. In case of multiple environments, specify their names separated by spaces. | --environments prod staging |
| --locales | Locales in which entries/assets will be published. In case of multiple locales, specify the codes separated by spaces. | --locales en-us fr-fr |
| -k, --stack-api-key | API key of the source stack. You must use either the --stack-api-key flag or the --alias flag. | -k blt********* |
| -a, --alias | Alias (name) of the management token. You must use either the --alias flag or the --stack-api-key flag. | -a my-token |
| Flag | Description | Default |
|---|---|---|
| --content-types | Content type UIDs to perform operation on. If not provided, operates on all content types. | - |
| --filter | Filter entries by status: draft, modified, unpublished, non-localized | - |
| --include-variants | Includes entry variants (alternate versions of a base entry) in the bulk operation. By default, only base entries are processed. | false |
| Filter values: |
| Flag | Description | Default |
|---|---|---|
| --publish-mode | Publish mode: bulk (uses Bulk Publish API) or single (individual API calls) | bulk |
| --branch | The name of the branch where you want to perform the bulk publish operation. If you don't mention the branch name, then by default the content from main branch will be published. | main |
| --source-env | Source environment for cross-publish | - |
| --source-alias | Alias name for source environment delivery token (required for cross-publish). Add delivery token using: csdx auth:tokens:add | - |
| -y, --yes | Set it to true to process the command with the current configuration. | false |
| -c, --config | The path of the optional configuration JSON file containing all the options for a single run. | - |
| --retry-failed | Use this option to retry publishing the failed entries/assets from the logfile. | - |
| --revert | Revert publish operations from a log folder. | - |
| --bulk-operation-file | Folder path to store operation logs. | bulk-operation |
1. Publish all content types to production
csdx cm:stacks:bulk-entries \
--operation publish \
--environments production \
--locales en-us \
-k blt*******
2. Publish specific content types to multiple environments
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post,article,page \
--environments dev,staging,production \
--locales en-us,es-es \
-k blt*******
3. Publish entries with non-localized field changes
csdx cm:stacks:bulk-entries \
--operation publish \
--filter non-localized \
--content-types blog_post \
--environments production \
-k blt*******
3. Publish only draft entries
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--filter draft \
--environments production \
--locales en-us \
-k blt*******
4. Cross-publish from production to staging
First, add the delivery token for the source environment (one-time setup):
# Add delivery token for production environment
csdx auth:tokens:add \
-a prod-delivery \
--delivery-token prod-delivery-token \
--stack-api-key blt******* \
--environment production \
--type delivery
Then use it for cross-publish:
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--source-env production \
--source-alias prod-delivery \
--environments staging \
--locales en-us \
-k blt*******
5. Publish using single mode for fine control
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--publish-mode single \
--environments production \
--locales en-us \
-k blt*******
6. Unpublish entries
csdx cm:stacks:bulk-entries \
--operation unpublish \
--content-types blog_post \
--environments staging \
--locales en-us \
-k blt*******
7. Retry failed operations
# Retry failed entries from a previous operation
csdx cm:stacks:bulk-entries \
--retry-failed ./bulk-operation
8. Revert a publish operation
# Revert (unpublish) previously published entries
csdx cm:stacks:bulk-entries \
--revert ./bulk-operation
9. Publish with entry variants
# Publish entries including their variants
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--include-variants \
--environments production \
--locales en-us \
-k blt*******
Perform bulk operations on assets with folder and file type filtering.
csdx cm:stacks:bulk-assets [OPTIONS]
Same as bulk entries (see above).
| Flag | Description | Example |
|---|---|---|
| --folder-uid | The UID of the Assets' folder from which the assets need to be published. Default: cs_root | --folder-uid cs_root |
1. Publish all assets
csdx cm:stacks:bulk-assets \
--operation publish \
--environments production \
--locales en-us \
-k blt*******
2. Publish assets from specific folder
csdx cm:stacks:bulk-assets \
--operation publish \
--folder-uid cs_product_images \
--environments production \
--locales en-us \
-k blt*******
3. Cross-publish assets from production to staging
csdx cm:stacks:bulk-assets \
--operation publish \
--source-env production \
--source-alias prod-delivery \
--environments staging \
--locales en-us \
-k blt*******
Note: See Example 4 in Bulk Entries section for how to set up the delivery token alias.
4. Unpublish assets
csdx cm:stacks:bulk-assets \
--operation unpublish \
--environments staging \
--locales en-us \
-k blt*******
5. Retry failed asset operations
# Retry failed assets from a previous operation
csdx cm:stacks:bulk-assets \
--retry-failed ./bulk-operation
6. Revert an asset publish operation
# Revert (unpublish) previously published assets
csdx cm:stacks:bulk-assets \
--revert ./bulk-operation
Perform bulk delete or move operations on assets stored in cs-assets spaces. This command uses the cs-assets REST API and is separate from the CMA-based Bulk Publish API used by bulk-entries and bulk-assets.
Prerequisite: Your region must have CS Assets enabled. See Configure Regions.
Warning: Do not combine CMS flags (--stack-api-key, --alias, --environments, --locales, --branch) with the CS Assets flags in the table below. The delete and move operations target the CS Assets API, not the CMS publish pipeline.
csdx cm:stacks:bulk-assets [OPTIONS]
| Flag | Description | Required |
|---|---|---|
| --operation | Operation type: delete or move | Yes |
| --space-uid | UID of the CS Assets space | Yes |
| --org-uid | Organization UID | Yes |
| --asset-uids-file | Path to a JSON file listing the assets to act on, in the form { "uids": ["uid1", "uid2"] } | Yes |
| --workspace | Workspace to operate in. Default: main | No |
| --locale | Locale to delete assets in. Required for delete and not accepted for move | For delete |
| --target-folder-uid | UID of the destination folder (move only) | For move |
| --folder-uid | UID of the source folder to filter assets | No |
| -y, --yes | Skip confirmation prompt | No |
Bulk delete assets in a space:
csdx cm:stacks:bulk-assets \
--operation delete \
--space-uid blt1234567890abcdef \
--org-uid bltorg1234567890 \
--locale en-us \
--asset-uids-file ./assets.json
Bulk move assets to a different folder:
csdx cm:stacks:bulk-assets \
--operation move \
--space-uid blt1234567890abcdef \
--org-uid bltorg1234567890 \
--folder-uid cs_source_folder \
--target-folder-uid cs_destination_folder \
--asset-uids-file ./assets.json \
--yes
Bulk publish or unpublish taxonomy terms. This command has no equivalent in CLI V1.
csdx cm:stacks:bulk-taxonomies [OPTIONS]| Flag | Description | Required |
|---|---|---|
| --operation | Operation type: publish or unpublish | Yes |
| --taxonomies | UIDs of the taxonomies to act on. Separate multiple UIDs with a space. | Yes |
| --environments | Environments to publish to or unpublish from | Yes |
| --locales | Locales to act on | Yes |
| --stack-api-key | API key of the target stack | Yes, unless --alias is used |
| --alias | Management token alias of the target stack | Yes, unless --stack-api-key is used |
| -y, --yes | Skip confirmation prompt | No |
Publish taxonomy terms to an environment:
csdx cm:stacks:bulk-taxonomies \
--operation publish \
--taxonomies product_category \
--environments production \
--locales en-us \
--stack-api-key bltxxxxxxUnpublish taxonomy terms:
csdx cm:stacks:bulk-taxonomies \
--operation unpublish \
--taxonomies product_category region \
--environments production \
--locales en-us \
--alias myaliasUses Contentstack's native Bulk Publish APIs.
Best For:
Example:
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--environments production \
--locales en-us \
-k blt*******
Makes individual API calls for each item with intelligent rate limiting.
Best For:
Example:
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--environments production \
--locales en-us \
--publish-mode single \
-k blt*******
Cross-publish allows you to promote content from one environment to another (e.g., staging → production). To use cross-publish, you need to set up a delivery token for the source environment.
Cross-publish uses the Delivery API to fetch only published content from the source environment. This ensures you're promoting exactly what's live, not draft content.
Step 1: Create a Delivery Token in Contentstack
Step 2: Add Token to CLI
csdx auth:tokens:add \
-a <alias-name> \
--delivery-token <your-delivery-token> \
--stack-api-key <your-stack-api-key> \
--environment <source-environment> \
--type delivery
Example:
# Add production delivery token
csdx auth:tokens:add \
-a prod-delivery \
--delivery-token prod-delivery-token \
--stack-api-key blt******** \
--environment production \
--type delivery
# Add staging delivery token
csdx auth:tokens:add \
-a staging-delivery \
--delivery-token stag-delivery-token \
--stack-api-key blt******** \
--environment staging \
--type delivery
Step 3: Use in Cross-Publish Operations
# Promote from staging to production
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post \
--source-env staging \
--source-alias staging-delivery \
--environments production \
--locales en-us \
-k blt********
# List all stored tokens (including delivery tokens)
csdx auth:tokens
# Remove a delivery token
csdx auth:tokens:remove -a staging-delivery
Scenario: A critical bug is discovered in 500 blog posts that need immediate unpublishing from production.
Solution:
csdx cm:stacks:bulk-entries \
--operation unpublish \
--content-types blog_post \
--environments production \
--locales en-us,es-es,fr-fr \
--yes \
-k blt********
Result: All 500 blog posts unpublished across 3 locales in ~2 minutes (bulk mode).
Scenario: Publish 200 product entries to production every Monday at 9 AM.
Solution: Create a cron job with a config file
# config.json
{
"operation": "publish",
"contentTypes": ["product"],
"environments": ["production"],
"locales": ["en-us"],
"filter": "modified",
"publishMode": "bulk"
}
# Cron command
0 9 * * 1 csdx cm:stacks:bulk-entries -c /path/to/config.json -k $STACK_API_KEY --yes
Scenario: Promote all content published in staging to production.
Setup (one-time):
# Add delivery token for staging environment
csdx auth:tokens:add \
-a staging-delivery \
--delivery-token blt_staging_token \
--stack-api-key blt******** \
--environment staging \
--type delivery
Solution:
# Promote entries
csdx cm:stacks:bulk-entries \
--operation publish \
--source-env staging \
--source-alias staging-delivery \
--environments production \
--locales en-us \
-k blt********
# Promote assets
csdx cm:stacks:bulk-assets \
--operation publish \
--source-env staging \
--source-alias staging-delivery \
--environments production \
--locales en-us \
-k blt********
Result: All staging content promoted to production with verification.
How it works: The --source-alias uses a stored delivery token to fetch only published content from the source environment. This ensures you're promoting exactly what's live in staging.
Scenario: Publish only draft entries that have never been published.
Solution:
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types article,blog_post \
--filter draft \
--environments production \
--locales en-us \
-k blt********
Result: Only unpublished entries are sent to production, saving API calls.
Scenario: Launch 50 new product entries across 10 locales and 3 environments simultaneously.
Solution:
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types product \
--environments dev,staging,production \
--locales en-us,es-es,fr-fr,de-de,it-it,pt-br,ja-jp,zh-cn,ko-kr,ar-ae \
--publish-mode bulk \
-k blt********
Result: 50 products × 10 locales × 3 envs = 1,500 publish operations completed in ~5 minutes via batching.
Scenario: A network issue caused 20 out of 200 entries to fail publishing.
Solution:
# Retry only failed entries
csdx cm:stacks:bulk-entries \
--retry-failed bulk-operation/failed-20250112-143022.json \
-k blt********
Result: Only the 20 failed entries are retried, successful ones are skipped.
Scenario: Publish only marketing assets in a specific folder.
Solution:
csdx cm:stacks:bulk-assets \
--operation publish \
--folder-uid cs_marketing_2026 \
--environments production \
--locales en-us \
-k blt********
Result: Only assets in the cs_marketing_2026 folder are published.
Scenario: After publishing 300 blog entries to production, you discover that some entries contain incorrect information and need to be unpublished immediately.
Solution:
# Step 1: Revert the publish operation using the operation log
csdx cm:stacks:bulk-entries \
--revert ./bulk-operation
How it works:
Result: All 300 blog entries are unpublished and reverted to their previous version, effectively rolling back the entire operation.
Important Notes:
cp -r bulk-operation bulk-operation-backup-$(date +%Y%m%d-%H%M%S)
Scenario: While publishing 500 product entries to production, a temporary network issue caused 75 entries to fail. You want to retry only the failed entries without re-processing the successful ones.
Solution:
# Step 1: Check the operation summary to identify failures
# The CLI automatically logs failed operations
# Step 2: Retry only the failed entries
csdx cm:stacks:bulk-entries \
--retry-failed ./bulk-operation
How it works:
Result: The 75 failed entries are retried and successfully published, while the 425 successful entries are untouched.
Best Practices:
Common Retry Scenarios:
Non-retryable Scenarios (require manual intervention):
Scenario: You have a CI/CD pipeline that publishes content, and you want to automatically retry failures with custom retry logic.
Solution:
# config.json
{
"operation": "publish",
"contentTypes": ["product", "article"],
"environments": ["production"],
"locales": ["en-us", "es-es"],
"publishMode": "bulk",
"maxRetries": 3,
"retryDelay": 5000,
"rateLimit": {
"requestsPerSecond": 10,
"maxConcurrent": 3
}
}
# Initial publish attempt
csdx cm:stacks:bulk-entries -c config.json -k blt******** --yes
# If failures occur, automatically retry after 30 seconds
sleep 30
csdx cm:stacks:bulk-entries --retry-failed ./bulk-operation --yes
Configuration Options:
Result: Failed operations are automatically retried with controlled rate limiting, making the process resilient to transient failures.
Scenario: Deploy content to staging for testing, and if approved, promote to production. If issues are found in production, quickly rollback.
Solution:
# Step 1: Publish to staging
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post,article \
--environments staging \
--locales en-us \
-k blt********
# Backup the operation logs
cp -r bulk-operation bulk-operation-staging-$(date +%Y%m%d-%H%M%S)
# Step 2: After testing, promote to production
csdx cm:stacks:bulk-entries \
--operation publish \
--content-types blog_post,article \
--environments production \
--locales en-us \
-k blt********
# Backup production operation logs
cp -r bulk-operation bulk-operation-production-$(date +%Y%m%d-%H%M%S)
# Step 3: If issues found, rollback production
csdx cm:stacks:bulk-entries \
--revert ./bulk-operation-production-YYYYMMDD-HHMMSS
Result: Content is safely deployed with the ability to quickly rollback production while preserving staging state.
The CLI supports various filters to target specific entries based on their status and content state:
The non-localized filter identifies entries where non-localized fields have been updated in the master locale, requiring localized entries to be republished to maintain consistency.
How it works:
Use Case: When you update a non-localized field (like a status, category, or global setting) in the master locale, all localized versions of that entry should be republished to reflect the change.
Example:
# Publish entries where non-localized fields have been updated
csdx cm:stacks:bulk-entries \
--operation publish \
--filter non-localized \
--content-types product \
--environments production \
-k blt*******
Note: This filter automatically discovers all locales in your stack and doesn't require specifying the --locales flag.
Instead of passing flags, use a JSON configuration file:
csdx cm:stacks:bulk-entries -c my-config.json -k blt********
Example Config File:
{
"operation": "publish",
"contentTypes": ["blog_post", "article"],
"environments": ["production", "staging"],
"locales": ["en-us", "es-es"],
"filter": "draft",
"publishMode": "bulk",
"branch": "main",
"rateLimit": {
"requestsPerSecond": 15,
"maxConcurrent": 5
},
"maxRetries": 3
}
Solution: The adaptive rate limiter should prevent this, but if it occurs:
Solution:
Solution:
Solution:
Error: Invalid configuration: errors
Solution: Validate your config file or flags:
Error: Cross-publish requires --source-alias flag with a delivery token
Solution:
csdx auth:tokens:add \
-a <alias-name> \
--delivery-token <token> \
--stack-api-key <api-key> \
--environment <source-env> \
--type delivery
Error: No token found for alias 'staging-delivery'
Solution:
Error: Alias 'my-token' is not a delivery token (type: management)
Solution: You're using a management token alias instead of a delivery token. Cross-publish requires a delivery token:
Always test bulk operations in dev/staging before production:
# Test in dev first
csdx cm:stacks:bulk-entries --operation publish --environments dev ...
# Then staging
csdx cm:stacks:bulk-entries --operation publish --environments staging ...
# Finally production
csdx cm:stacks:bulk-entries --operation publish --environments production ...
For operations with many flags, use config files for repeatability and version control.
Pay attention to rate limiter messages to understand system behavior.
Use bulk mode by default for efficiency, switch to single mode only when needed.
Save log files for auditing and potential rollback:
cp -r bulk-operation bulk-operation-backup-$(date +%Y%m%d)
Apply filters to publish only necessary content:
--filter draft # Only unpublished entries
--filter modified # Only entries modified since last publish
Group operations by content type, environment, or locale for better performance.