Management SDK - Content

The ContentMethods class provides comprehensive functionality for managing content items, including creation, publication, approval workflows, and retrieval operations.

Bulk Operations Overview

When working with multiple content items, use the bulk methods for better performance:

Single Item MethodBulk MethodUse Case
saveContentItem()saveContentItems(contentItems[])Creating/updating multiple items
publishContent()batchWorkflowContent(contentIDs[])Publishing multiple items at once
unPublishContent()batchWorkflowContent(contentIDs[])Unpublishing multiple items at once

Important:

  • Bulk methods accept arrays - pass ALL your items/IDs in a single call
  • Returns content IDs in the same order as the input array, allowing you to correlate results with your original items

Function List

FunctionDescription
getContentItemRetrieves a specific content item by ID
publishContentPublishes a single content item
unPublishContentUnpublishes a single content item
batchWorkflowContentBulk workflow operation on multiple content items
contentRequestApprovalRequests approval for a content item
approveContentApproves a content item
declineContentDeclines a content item
deleteContentDeletes a content item
saveContentItemSaves a single content item
saveContentItemsSaves multiple content items (bulk)
getContentItemsRetrieves content items with filtering (deprecated)
getContentListRetrieves content list with advanced filtering
getContentHistoryRetrieves content item history
getContentCommentsRetrieves content item comments

getContentItem

Retrieves a specific content item by ID and locale.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to retrieve
guidstringYesThe website GUID
localestringYesThe locale code (e.g., 'en-us')

Returns

Promise<ContentItem> - The content item object

Usage Example

const contentItem = await client.contentMethods.getContentItem(123, "your-guid", "en-us")
console.log(contentItem.fields.title)

publishContent

Publishes a single content item through the batch workflow system.

Note: For publishing multiple items, use batchWorkflowContent() with WorkflowOperationType.Publish instead.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to publish
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the publish operation
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array containing the content ID that was published

Usage Example

// Publish a single item
const publishedIds = await client.contentMethods.publishContent(123, "your-guid", "en-us", "Publishing update")
console.log("Published ID:", publishedIds[0])

// Return batch ID immediately for custom polling
const batchId = await client.contentMethods.publishContent(123, "your-guid", "en-us", null, true)

unPublishContent

Unpublishes a single content item through the batch workflow system.

Note: For unpublishing multiple items, use batchWorkflowContent() with WorkflowOperationType.Unpublish instead.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to unpublish
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the unpublish operation
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array containing the content ID that was unpublished

Usage Example

const unpublishedIds = await client.contentMethods.unPublishContent(123, "your-guid", "en-us", "Temporary unpublish")
console.log("Unpublished ID:", unpublishedIds[0])

batchWorkflowContent

Recommended for bulk workflow operations. Performs a batch workflow operation on multiple content items at once. Supports Publish, Unpublish, Approve, Decline, and RequestApproval operations.

Pass ALL your content IDs in a single call instead of calling publishContent() or unPublishContent() in a loop.

Parameters

ParameterTypeRequiredDescription
contentIDsnumber[]YesArray of ALL content IDs to process (e.g., [101, 102, 103, 104, ...])
guidstringYesThe website GUID
localestringYesThe locale code
operationWorkflowOperationTypeYesThe workflow operation (see below)
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

WorkflowOperationType Values

ValueDescription
WorkflowOperationType.PublishPublish content items
WorkflowOperationType.UnpublishUnpublish content items
WorkflowOperationType.ApproveApprove content items
WorkflowOperationType.DeclineDecline content items
WorkflowOperationType.RequestApprovalRequest approval for content items

Returns

Promise<number[]> - Array of content IDs that were processed, in the same order as the input array

Important Notes

  • Pass Multiple IDs: This method accepts an array of content IDs - pass ALL your IDs in one call (e.g., [101, 102, 103, 104, 105]), not one at a time.
  • Order Preservation: The returned content IDs array maintains the same order as your input contentIDs array.
  • Performance: Always use this method for bulk operations instead of calling single-item methods in a loop.
  • Atomic Operation: All items are processed as a single batch operation.

Usage Examples

Publish Multiple Items at Once

import {WorkflowOperationType} from "@agility/management-sdk"

// Pass ALL content IDs you want to publish in a single array
const contentIDsToPublish = [101, 102, 103, 104, 105, 106, 107, 108, 109, 110]

const publishedIds = await client.contentMethods.batchWorkflowContent(
	contentIDsToPublish, // All IDs in one call
	"your-guid",
	"en-us",
	WorkflowOperationType.Publish,
)

// Returns all IDs in the same order: [101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
console.log("Published:", publishedIds)

Bulk Publish After Save

import {WorkflowOperationType} from "@agility/management-sdk"

// First, bulk save content items
const contentItems: ContentItem[] = [
	{
		contentID: -1,
		properties: {definitionName: "Article", referenceName: "articles"},
		fields: {title: "Article 1"},
	},
	{
		contentID: -1,
		properties: {definitionName: "Article", referenceName: "articles"},
		fields: {title: "Article 2"},
	},
	{
		contentID: -1,
		properties: {definitionName: "Article", referenceName: "articles"},
		fields: {title: "Article 3"},
	},
]

// Save all items - returns IDs in same order
const savedIds = await client.contentMethods.saveContentItems(contentItems, "your-guid", "en-us")
console.log("Saved IDs:", savedIds) // e.g., [101, 102, 103]

// Publish all saved items - returns IDs in same order
const publishedIds = await client.contentMethods.batchWorkflowContent(
	savedIds,
	"your-guid",
	"en-us",
	WorkflowOperationType.Publish,
)
console.log("Published IDs:", publishedIds) // [101, 102, 103] - same order

// Correlate results
savedIds.forEach((id, index) => {
	console.log(`Item "${contentItems[index].fields.title}" saved as ID ${id} and published`)
})

Bulk Unpublish

const contentIDs = [101, 102, 103]

const unpublishedIds = await client.contentMethods.batchWorkflowContent(
	contentIDs,
	"your-guid",
	"en-us",
	WorkflowOperationType.Unpublish,
)

// unpublishedIds[0] = 101
// unpublishedIds[1] = 102
// unpublishedIds[2] = 103

Workflow Approval Process

const contentIDs = [201, 202, 203]

// Step 1: Request approval for all items
const requestedIds = await client.contentMethods.batchWorkflowContent(
	contentIDs,
	"your-guid",
	"en-us",
	WorkflowOperationType.RequestApproval,
)
console.log("Approval requested for:", requestedIds)

// Step 2: Approve all items (typically done by an approver)
const approvedIds = await client.contentMethods.batchWorkflowContent(
	contentIDs,
	"your-guid",
	"en-us",
	WorkflowOperationType.Approve,
)
console.log("Approved:", approvedIds)

// Step 3: Publish approved items
const publishedIds = await client.contentMethods.batchWorkflowContent(
	approvedIds,
	"your-guid",
	"en-us",
	WorkflowOperationType.Publish,
)
console.log("Published:", publishedIds)

Decline Multiple Items

const contentIDs = [301, 302]

const declinedIds = await client.contentMethods.batchWorkflowContent(
	contentIDs,
	"your-guid",
	"en-us",
	WorkflowOperationType.Decline,
)
console.log("Declined content IDs:", declinedIds)

contentRequestApproval

Requests approval for a content item through the workflow system.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the approval request
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array of content IDs that had approval requested

Usage Example

const requestedIds = await client.contentMethods.contentRequestApproval(123, "your-guid", "en-us", "Ready for review")

approveContent

Approves a content item in the workflow system.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to approve
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the approval
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array of content IDs that were approved

Usage Example

const approvedIds = await client.contentMethods.approveContent(123, "your-guid", "en-us", "Approved for publication")

declineContent

Declines a content item in the workflow system.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to decline
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the decline
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array of content IDs that were declined

Usage Example

const declinedIds = await client.contentMethods.declineContent(123, "your-guid", "en-us", "Needs revision")

deleteContent

Deletes a content item through the batch workflow system.

Parameters

ParameterTypeRequiredDescription
contentIDnumberYesThe ID of the content item to delete
guidstringYesThe website GUID
localestringYesThe locale code
commentsstringNoOptional comments for the deletion
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array of content IDs that were deleted

Usage Example

const deletedIds = await client.contentMethods.deleteContent(123, "your-guid", "en-us", "Removing outdated content")

saveContentItem

Saves a single content item (create or update) through the batch workflow system.

Note: For bulk operations, use saveContentItems() instead for better performance.

Parameters

ParameterTypeRequiredDescription
contentItemContentItemYesThe content item object to save
guidstringYesThe website GUID
localestringYesThe locale code
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array containing the content ID that was saved

ContentItem Structure

interface ContentItem {
	contentID: number // Use -1 or 0 for new items, existing ID for updates
	properties: {
		definitionName: string // The content model name
		referenceName: string // The container reference name
		itemOrder?: number // Optional ordering
		releaseDate?: string // Optional scheduled release date
		pullDate?: string // Optional scheduled pull date
	}
	fields: {
		[key: string]: any // Field values matching the content model
	}
	seo?: {
		// Optional SEO properties
		metaDescription?: string
		metaKeywords?: string
		metaHTML?: string
		menuVisible?: boolean
		sitemapVisible?: boolean
	}
	scripts?: {
		// Optional custom scripts
		top?: string
		bottom?: string
	}
}

Usage Example

// Create a new content item
const newItem: ContentItem = {
	contentID: -1, // -1 or 0 for new items
	properties: {
		definitionName: "BlogPost",
		referenceName: "blogposts",
	},
	fields: {
		title: "New Article",
		content: "Article content...",
		author: "John Doe",
		publishDate: "2024-01-15",
	},
	seo: {
		metaDescription: "A great new article",
	},
}

const savedIds = await client.contentMethods.saveContentItem(newItem, "your-guid", "en-us")
console.log("Created content ID:", savedIds[0])

// Update an existing content item
const existingItem = await client.contentMethods.getContentItem(123, "your-guid", "en-us")
existingItem.fields.title = "Updated Title"
const updatedIds = await client.contentMethods.saveContentItem(existingItem, "your-guid", "en-us")

saveContentItems

Recommended for bulk operations. Saves multiple content items in a single batch operation.

Parameters

ParameterTypeRequiredDescription
contentItemsContentItem[]YesArray of content item objects to save
guidstringYesThe website GUID
localestringYesThe locale code
returnBatchIdbooleanNoIf true, returns batch ID immediately without waiting

Returns

Promise<number[]> - Array of content IDs that were saved, in the same order as the input array

Important Notes

  • Order Preservation: The returned content IDs array maintains the same order as your input contentItems array. This allows you to correlate each returned ID with its corresponding input item.
  • Performance: For bulk operations (2+ items), always use saveContentItems() instead of calling saveContentItem() in a loop.
  • Mixed Operations: You can mix new items (contentID: -1) and updates (existing contentID) in the same batch.

Note: If you receive a -1 as part of the return array, that means there was a problem in saving your item. We are working improving our messaging to get you the exact error message in those cases, but until then, the recommendation is to log that save failure and check into manually.

Usage Examples

Basic Bulk Save

const contentItems: ContentItem[] = [
	{
		contentID: -1,
		properties: {definitionName: "BlogPost", referenceName: "blogposts"},
		fields: {title: "Article 1", slug: "article-1"},
	},
	{
		contentID: -1,
		properties: {definitionName: "BlogPost", referenceName: "blogposts"},
		fields: {title: "Article 2", slug: "article-2"},
	},
	{
		contentID: -1,
		properties: {definitionName: "BlogPost", referenceName: "blogposts"},
		fields: {title: "Article 3", slug: "article-3"},
	},
]

const savedIds = await client.contentMethods.saveContentItems(contentItems, "your-guid", "en-us")

// savedIds[0] corresponds to 'Article 1'
// savedIds[1] corresponds to 'Article 2'
// savedIds[2] corresponds to 'Article 3'
console.log("Created IDs:", savedIds)

Correlating Results with Input Data

// Example: Import products and track their new IDs
const products = [
	{sku: "SKU-001", name: "Product A", price: 29.99},
	{sku: "SKU-002", name: "Product B", price: 49.99},
	{sku: "SKU-003", name: "Product C", price: 19.99},
]

const contentItems: ContentItem[] = products.map((product) => ({
	contentID: -1,
	properties: {definitionName: "Product", referenceName: "products"},
	fields: {
		sku: product.sku,
		name: product.name,
		price: product.price,
	},
}))

const savedIds = await client.contentMethods.saveContentItems(contentItems, "your-guid", "en-us")

// Map the returned IDs back to original products (same order!)
const productsWithIds = products.map((product, index) => ({
	...product,
	contentID: savedIds[index],
}))

console.log(productsWithIds)
// [
//   { sku: 'SKU-001', name: 'Product A', price: 29.99, contentID: 101 },
//   { sku: 'SKU-002', name: 'Product B', price: 49.99, contentID: 102 },
//   { sku: 'SKU-003', name: 'Product C', price: 19.99, contentID: 103 }
// ]

Bulk Save and Publish

// Step 1: Save multiple items
const contentItems: ContentItem[] = [
	{
		contentID: -1,
		properties: {definitionName: "NewsArticle", referenceName: "news"},
		fields: {title: "Breaking News 1", content: "..."},
	},
	{
		contentID: -1,
		properties: {definitionName: "NewsArticle", referenceName: "news"},
		fields: {title: "Breaking News 2", content: "..."},
	},
]

const savedIds = await client.contentMethods.saveContentItems(contentItems, "your-guid", "en-us")
console.log("Saved content IDs:", savedIds) // e.g., [201, 202]

// Step 2: Publish all saved items using batchWorkflowContent
import {WorkflowOperationType} from "@agility/management-sdk"

const publishedIds = await client.contentMethods.batchWorkflowContent(
	savedIds,
	"your-guid",
	"en-us",
	WorkflowOperationType.Publish,
)
console.log("Published content IDs:", publishedIds) // [201, 202] - same order

Mixed Create and Update Operations

const contentItems: ContentItem[] = [
	// New item
	{
		contentID: -1,
		properties: {definitionName: "BlogPost", referenceName: "blogposts"},
		fields: {title: "Brand New Post"},
	},
	// Update existing item
	{
		contentID: 456, // Existing content ID
		properties: {definitionName: "BlogPost", referenceName: "blogposts"},
		fields: {title: "Updated Existing Post"},
	},
]

const savedIds = await client.contentMethods.saveContentItems(contentItems, "your-guid", "en-us")
// savedIds[0] = new ID for 'Brand New Post'
// savedIds[1] = 456 (the updated item retains its ID)

getContentItems

Retrieves content items with basic filtering parameters (deprecated - use getContentList instead).

Parameters

ParameterTypeRequiredDescription
referenceNamestringYesThe reference name of the content model
guidstringYesThe website GUID
localestringYesThe locale code
listParamsListParamsYesPagination and filtering parameters

Returns

Promise<ContentList> - The content list with items and pagination info

Usage Example

const listParams = {
	take: 10,
	skip: 0,
	sortField: "title",
	sortDirection: "asc",
}

const contentList = await client.contentMethods.getContentItems("articles", "your-guid", "en-us", listParams)

getContentList

Retrieves content items with advanced filtering using POST request with filter object.

Parameters

ParameterTypeRequiredDescription
referenceNamestringYesThe reference name of the content model
guidstringYesThe website GUID
localestringYesThe locale code
listParamsListParamsYesPagination and filtering parameters
filterObjectContentListFilterModelNoAdvanced filter criteria

Returns

Promise<ContentList> - The content list with items and pagination info

Usage Example

const listParams = {
	take: 20,
	skip: 0,
	sortField: "dateCreated",
	sortDirection: "desc",
	showDeleted: false,
}

const filterObject = {
	publishedState: "published",
	searchText: "important",
}

const contentList = await client.contentMethods.getContentList(
	"articles",
	"your-guid",
	"en-us",
	listParams,
	filterObject,
)

getContentHistory

Retrieves the history of changes for a specific content item.

Parameters

ParameterTypeRequiredDescription
localestringYesThe locale code
guidstringYesThe website GUID
contentIDnumberYesThe ID of the content item
takenumberNoNumber of history entries to retrieve (default: 50)
skipnumberNoNumber of history entries to skip (default: 0)

Returns

Promise<ContentItemHistory> - The content item history with pagination

Usage Example

const history = await client.contentMethods.getContentHistory("en-us", "your-guid", 123, 25, 0)
console.log(history.items.length)

getContentComments

Retrieves comments for a specific content item.

Parameters

ParameterTypeRequiredDescription
localestringYesThe locale code
guidstringYesThe website GUID
contentIDnumberYesThe ID of the content item
takenumberNoNumber of comments to retrieve (default: 50)
skipnumberNoNumber of comments to skip (default: 0)

Returns

Promise<ItemComments> - The content item comments with pagination

Usage Example

const comments = await client.contentMethods.getContentComments("en-us", "your-guid", 123, 10, 0)
console.log(comments.items.length)

Best Practices for Imports and Syncs

When importing content from external systems or syncing with third-party data sources, follow these guidelines for optimal performance and reliability.

  1. Do all lookups first - Check which items already exist before making any changes
  2. Batch your saves - Use saveContentItems() to save multiple items in a single call
  3. Batch your publishes - Use batchWorkflowContent() to publish all saved items at once
  4. Use single-threaded processing - Process batches sequentially, not in parallel

Why Avoid Parallelism?

Do NOT use parallel API calls to speed up imports. While it may seem faster, parallel requests can:

  • Overwhelm the API and cause rate limiting
  • Lead to race conditions and inconsistent state
  • Result in batch conflicts and failed operations

Instead, batch your updates in chunks and process them sequentially in a single thread.

Strategy for Large Imports

For large datasets or ongoing syncs with external systems:

import {WorkflowOperationType} from "@agility/management-sdk"

async function syncExternalData(externalItems: ExternalItem[]) {
	const guid = "your-guid"
	const locale = "en-us"

	// Step 1: Pull existing content list and build a lookup map
	const existingContent = await client.contentMethods.getContentList(
		"products",
		guid,
		locale,
		{take: 1000, skip: 0}, // Adjust based on your data size
	)

	// Build a map for fast duplicate checking (key = external ID or SKU)
	const existingMap = new Map<string, number>()
	for (const item of existingContent.items) {
		if (item.fields.externalId) {
			existingMap.set(item.fields.externalId, item.contentID)
		}
	}

	// Step 2: Separate items into creates vs updates
	const itemsToCreate: ContentItem[] = []
	const itemsToUpdate: ContentItem[] = []

	for (const ext of externalItems) {
		const existingId = existingMap.get(ext.externalId)

		if (existingId) {
			// Update existing item
			itemsToUpdate.push({
				contentID: existingId,
				properties: {definitionName: "Product", referenceName: "products"},
				fields: {name: ext.name, price: ext.price, externalId: ext.externalId},
			})
		} else {
			// Create new item
			itemsToCreate.push({
				contentID: -1,
				properties: {definitionName: "Product", referenceName: "products"},
				fields: {name: ext.name, price: ext.price, externalId: ext.externalId},
			})
		}
	}

	// Step 3: Process in batches (recommended batch size: 50-100 items)
	const BATCH_SIZE = 50
	const allSavedIds: number[] = []

	// Process creates in batches
	for (let i = 0; i < itemsToCreate.length; i += BATCH_SIZE) {
		const batch = itemsToCreate.slice(i, i + BATCH_SIZE)
		const savedIds = await client.contentMethods.saveContentItems(batch, guid, locale)
		allSavedIds.push(...savedIds)
		console.log(`Created batch ${Math.floor(i / BATCH_SIZE) + 1}: ${savedIds.length} items`)
	}

	// Process updates in batches
	for (let i = 0; i < itemsToUpdate.length; i += BATCH_SIZE) {
		const batch = itemsToUpdate.slice(i, i + BATCH_SIZE)
		const savedIds = await client.contentMethods.saveContentItems(batch, guid, locale)
		allSavedIds.push(...savedIds)
		console.log(`Updated batch ${Math.floor(i / BATCH_SIZE) + 1}: ${savedIds.length} items`)
	}

	// Step 4: Publish all saved items in batches
	for (let i = 0; i < allSavedIds.length; i += BATCH_SIZE) {
		const batch = allSavedIds.slice(i, i + BATCH_SIZE)
		await client.contentMethods.batchWorkflowContent(batch, guid, locale, WorkflowOperationType.Publish)
		console.log(`Published batch ${Math.floor(i / BATCH_SIZE) + 1}: ${batch.length} items`)
	}

	return {
		created: itemsToCreate.length,
		updated: itemsToUpdate.length,
		published: allSavedIds.length,
	}
}

Strategy for Small Lists or Initial Imports

For smaller datasets or one-time initial imports, you can simplify by pulling the entire content list for local duplicate checking:

async function initialImport(sourceData: SourceItem[]) {
	const guid = "your-guid"
	const locale = "en-us"

	// For initial imports or small lists: pull everything and check locally
	const existing = await client.contentMethods.getContentList("articles", guid, locale, {take: 5000, skip: 0})

	// Build hashtable/map for O(1) duplicate lookups
	const existingBySlug = new Map(existing.items.map((item) => [item.fields.slug, item.contentID]))

	// Filter to only new items
	const newItems = sourceData.filter((item) => !existingBySlug.has(item.slug))

	if (newItems.length === 0) {
		console.log("No new items to import")
		return
	}

	// Transform and save all new items
	const contentItems = newItems.map((item) => ({
		contentID: -1,
		properties: {definitionName: "Article", referenceName: "articles"},
		fields: {title: item.title, slug: item.slug, body: item.body},
	}))

	const savedIds = await client.contentMethods.saveContentItems(contentItems, guid, locale)

	// Publish all at once
	const publishedIds = await client.contentMethods.batchWorkflowContent(
		savedIds,
		guid,
		locale,
		WorkflowOperationType.Publish,
	)

	console.log(`Imported and published ${publishedIds.length} new items`)
}

Summary

ScenarioStrategy
Large ongoing syncPull existing → build map → batch saves → batch publishes
Small list / initial importPull all existing → local duplicate check → single batch save → single batch publish
Any importAlways use sequential batching, never parallel requests

Complete Bulk Workflow Example

This example demonstrates a complete workflow for bulk creating, saving, and publishing content items while tracking the correlation between input data and resulting content IDs.

import agilityMgmt, {WorkflowOperationType} from "@agility/management-sdk"

async function bulkImportAndPublish() {
	const client = agilityMgmt.getApi({
		location: "USA",
		websiteId: "your-guid",
		securityKey: "your-api-key",
	})

	const guid = "your-guid"
	const locale = "en-us"

	// Source data to import
	const sourceData = [
		{externalId: "ext-001", title: "First Article", body: "Content 1..."},
		{externalId: "ext-002", title: "Second Article", body: "Content 2..."},
		{externalId: "ext-003", title: "Third Article", body: "Content 3..."},
		{externalId: "ext-004", title: "Fourth Article", body: "Content 4..."},
	]

	// Step 1: Transform source data into ContentItem objects
	const contentItems = sourceData.map((item) => ({
		contentID: -1, // New items
		properties: {
			definitionName: "Article",
			referenceName: "articles",
		},
		fields: {
			title: item.title,
			body: item.body,
			externalId: item.externalId, // Track original ID in a field
		},
	}))

	// Step 2: Bulk save all items using saveContentItems()
	console.log(`Saving ${contentItems.length} content items...`)
	const savedIds = await client.contentMethods.saveContentItems(contentItems, guid, locale)

	// savedIds are in the SAME ORDER as contentItems input
	console.log("Saved content IDs:", savedIds)

	// Step 3: Create a mapping of external IDs to Agility content IDs
	const idMapping = sourceData.map((item, index) => ({
		externalId: item.externalId,
		title: item.title,
		contentID: savedIds[index], // Same order guarantees correct mapping
	}))

	console.log("ID Mapping:")
	idMapping.forEach((m) => {
		console.log(`  ${m.externalId} -> contentID: ${m.contentID} ("${m.title}")`)
	})

	// Step 4: Bulk publish all saved items using batchWorkflowContent()
	console.log(`Publishing ${savedIds.length} content items...`)
	const publishedIds = await client.contentMethods.batchWorkflowContent(
		savedIds,
		guid,
		locale,
		WorkflowOperationType.Publish,
	)

	// publishedIds are in the SAME ORDER as savedIds input
	console.log("Published content IDs:", publishedIds)

	// Verify the order is maintained
	publishedIds.forEach((id, index) => {
		console.log(`Published: ${idMapping[index].title} (contentID: ${id})`)
	})

	return idMapping
}

// Run the import
bulkImportAndPublish()
	.then((mapping) => console.log("Import complete!", mapping))
	.catch((err) => console.error("Import failed:", err))

Output Example

Saving 4 content items...
Saved content IDs: [1001, 1002, 1003, 1004]
ID Mapping:
  ext-001 -> contentID: 1001 ("First Article")
  ext-002 -> contentID: 1002 ("Second Article")
  ext-003 -> contentID: 1003 ("Third Article")
  ext-004 -> contentID: 1004 ("Fourth Article")
Publishing 4 content items...
Published content IDs: [1001, 1002, 1003, 1004]
Published: First Article (contentID: 1001)
Published: Second Article (contentID: 1002)
Published: Third Article (contentID: 1003)
Published: Fourth Article (contentID: 1004)
Import complete!

Error Handling

All methods throw Exception objects on failure:

try {
	const contentItem = await client.contentMethods.getContentItem(123, "your-guid", "en-us")
} catch (error) {
	console.error("Failed to get content item:", error.message)
}