Agility CMS documentationAgility CMS documentation
OverviewEditorsDevelopersOwners & AdminsTraining GuideApps
Sign inLet's Chat
Management SDK
Containers & Lists

Containers

Containers & Lists

Read, create, update, and delete Agility containers (content lists) with the Management SDK, in both JavaScript and .NET.

In Agility CMS a Container is a content list. It links a content model to the content items stored against it, and its reference name is what you query when fetching content. A container can hold many items, or be configured to hold a single item.

Content Model (BlogPost) → Container (BlogPosts) → Content Items

All container operations live on the containerMethods group of the Management SDK client — apiClient.containerMethods in JavaScript, client.containerMethods in .NET.

Method reference

OperationJavaScript.NET
List all containersgetContainerList(guid)GetContainerList(guid)
List containers (paged)getContainerListPaged(...)not available
Get by IDgetContainerByID(id, guid)GetContainerById(id, guid)
Get by reference namegetContainerByReferenceName(referenceName, guid)GetContainerByReferenceName(referenceName, guid)
Get containers by modelgetContainersByModel(modelId, guid)GetContainersByModel(modelId, guid)
Get security settingsgetContainerSecurity(id, guid)GetContainerSecurity(id, guid)
Get notificationsgetNotificationList(id, guid)GetNotificationList(id, guid)
Create or updatesaveContainer(container, guid, forceReferenceName)SaveContainer(container, guid)
DeletedeleteContainer(id, guid)DeleteContainer(id, guid)

Only one method differs between the SDKs: paged listing exists in JavaScript and not in .NET. Everything else is available in both.

Reading containers

Get all containers

Returns every container in the instance.

const containers = await apiClient.containerMethods.getContainerList(guid);

containers.forEach(container => {
  console.log(`Container: ${container.referenceName}`);
  console.log(`- Model ID: ${container.contentDefinitionID}`);
  console.log(`- Container ID: ${container.contentViewID}`);
});
var containers = await client.containerMethods.GetContainerList(guid);

foreach (var container in containers)
{
    Console.WriteLine($"{container.ReferenceName} (ID: {container.ContentViewID})");
}

.NET signature: Task<List<Container?>> GetContainerList(string guid)

Get containers (paged)

Returns a paginated result with a total count, and supports filtering by container type and modification date. This one is JavaScript only.

import { ContentViewType } from '@agility/management-sdk';

const paged = await apiClient.containerMethods.getContainerListPaged(
  guid,
  20,                   // pageSize
  0,                    // recordOffset
  ContentViewType.All,  // contentType
  true,                 // includeModules
  new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // updatedSince (optional)
);

console.log('Total containers:', paged.totalCount);
paged.items.forEach(c => console.log(c.referenceName));

Signature: getContainerListPaged(guid: string, pageSize?: number, recordOffset?: number, contentType?: ContentViewType, includeModules?: boolean, updatedSince?: Date): Promise<PagedResult<Container>>

ParameterDefaultDescription
guid—Instance GUID.
pageSize20Number of containers per page.
recordOffset0Number of records to skip.
contentTypeAllFilters the type of container returned. ContentViewType values are All, Shared, Linked, DynamicPageList.
includeModulestrueWhether module (component) containers are included.
updatedSince—Only return containers modified after this date.

Not available in the .NET SDK. Call GET /api/v1/instance/{guid}/container/list/paged directly instead.

Get a container by ID

const container = await apiClient.containerMethods.getContainerByID(
  contentViewID, // Container ID
  guid          // Instance GUID
);

console.log('Container details:', container);
var container = await client.containerMethods.GetContainerById(contentViewID, guid);
Console.WriteLine($"Container: {container?.ReferenceName}");

.NET signature: Task<Container?> GetContainerById(int? id, string guid)

Note the casing difference: JavaScript exports getContainerByID, .NET exposes GetContainerById.

Get a container by reference name

This is the usual way to look up a container, since the reference name is the identifier you use elsewhere in the API. The JavaScript method is typed to return Container | null and resolves to null on a 404 rather than throwing, so check the result before using it.

const container = await apiClient.containerMethods.getContainerByReferenceName(
  'BlogPosts', // Container reference name
  guid         // Instance GUID
);

if (container) {
  console.log('Found container:', container.referenceName);
} else {
  console.log('Container not found');
}
var container = await client.containerMethods.GetContainerByReferenceName("BlogPosts", guid);

if (container != null)
{
    Console.WriteLine($"Found: {container.ReferenceName} (ID: {container.ContentViewID})");
}

.NET signature: Task<Container?> GetContainerByReferenceName(string? referenceName, string guid)

Get containers by model

Finds every container based on a specific content model — useful before changing or deleting a model.

const containers = await apiClient.containerMethods.getContainersByModel(modelId, guid);
containers.forEach(c => console.log(c.referenceName));
var containers = await client.containerMethods.GetContainersByModel(modelId, guid);

foreach (var container in containers)
{
    Console.WriteLine($"Container: {container?.ReferenceName}");
}

.NET signature: Task<List<Container?>> GetContainersByModel(int? modelId, string guid)

Get container security settings

const security = await apiClient.containerMethods.getContainerSecurity(contentViewID, guid);
var security = await client.containerMethods.GetContainerSecurity(contentViewID, guid);

.NET signature: Task<Container?> GetContainerSecurity(int? id, string guid)

The returned Container carries the currentUserCan* flags — currentUserCanEdit, currentUserCanDelete, currentUserCanPublish, and so on — which tell you what the authenticated token is allowed to do with this container.

Get container notifications

Returns the notification recipients configured on a container.

const notifications = await apiClient.containerMethods.getNotificationList(contentViewID, guid);
notifications.forEach(n => console.log(n.emailAddress));
var notifications = await client.containerMethods.GetNotificationList(contentViewID, guid);

foreach (var notification in notifications)
{
    Console.WriteLine(notification?.EmailAddress);
}

.NET signature: Task<List<Notification?>> GetNotificationList(int? id, string guid)

Creating and updating containers

Both SDKs use a single save method for create and update. Set the container ID to -1 to create a new container; pass an existing container ID to update that container.

Container fields

The Container object is wide — these are the fields that matter when creating one:

JavaScript.NETDescription
contentViewIDContentViewIDContainer ID. Use -1 for a new container.
referenceNameReferenceNameUnique reference name used to query the container.
contentViewNameContentViewNameThe container's name.
titleTitleDisplay title shown in the CMS.
contentDefinitionIDContentDefinitionIDID of the content model the container is based on.
contentDefinitionTypeIDContentDefinitionTypeIDThe model's type — not its ID. See the note below.
defaultSortColumnDefaultSortColumnColumn the CMS listing sorts by.
defaultSortDirectionDefaultSortDirectionasc or desc.
numRowsInListingNumRowsInListingRows shown per page in the CMS listing.
isDynamicPageListIsDynamicPageListWhether the container drives dynamic pages.
requiresApprovalRequiresApprovalWhether items need approval before publishing.

There is no settings object on a container. All of these are top-level properties. If you've seen a settings: { ... } payload in older examples, it was never part of the contract — the API ignores it, so sort order and page size set that way silently do nothing.

contentDefinitionTypeID is a model type, not a model ID. The Management API publishes the numeric values at GET /api/v1/types under contentModelTypes: Item = 0, List = 1, Module = 2. The JavaScript SDK also ships a ContentDefinitionTypeID enum, but its numbering does not currently line up with the API's — so pass the integer from /api/v1/types rather than relying on the enum, and check the value on an existing container before creating a new one.

The most reliable way to build a container payload is to read one you already have and mirror its shape:

const existing = await apiClient.containerMethods.getContainerByReferenceName('BlogPosts', guid);
console.log(JSON.stringify(existing, null, 2)); // copy the shape from a container that works

Create a container

Look up the content model first so you can link the container to it.

// First, get the content model
const model = await apiClient.modelMethods.getModelByReferenceName('BlogPost', guid);

if (!model) {
  throw new Error('Content model not found');
}

const containerPayload = {
  contentViewID: -1,        // -1 for new containers
  referenceName: 'BlogPosts',
  contentViewName: 'Blog Posts',
  title: 'Blog Posts',
  contentDefinitionID: model.id, // link to the model
  contentDefinitionTypeID: 1     // 1 = List (see /api/v1/types)
};

const savedContainer = await apiClient.containerMethods.saveContainer(
  containerPayload,
  guid,
  false // forceReferenceName
);

console.log('Created container:', savedContainer.referenceName);
using agility.models;

// First, get the content model to link
var model = await client.modelMethods.GetModelByReferenceName("BlogPost", guid);

var newContainer = new Container
{
    ContentViewID = -1,
    ReferenceName = "BlogPosts",
    ContentViewName = "Blog Posts",
    Title = "Blog Posts",
    ContentDefinitionID = model.ID,
    ContentDefinitionTypeID = ContentDefinitionTypeID.List
};

var saved = await client.containerMethods.SaveContainer(newContainer, guid);
Console.WriteLine($"Created container: {saved?.ReferenceName}");

.NET signature: Task<Container?> SaveContainer(Container container, string guid)

The JavaScript saveContainer takes a third forceReferenceName argument. When false, Agility may adjust the reference name to keep it unique; when true, the reference name you supplied is used as-is. There is no .NET equivalent.

Update a container

Retrieve the container, change the properties you need, and save it back. Keep the existing contentViewID so the save is treated as an update.

const existing = await apiClient.containerMethods.getContainerByReferenceName(
  'BlogPosts',
  guid
);

if (existing) {
  existing.title = 'Blog Posts (Archive)';
  existing.numRowsInListing = 50;
  existing.defaultSortColumn = 'Date';
  existing.defaultSortDirection = 'desc';

  const updated = await apiClient.containerMethods.saveContainer(existing, guid, false);
  console.log('Updated container:', updated.referenceName);
}
var existing = await client.containerMethods.GetContainerByReferenceName("BlogPosts", guid);

if (existing != null)
{
    existing.Title = "Blog Posts (Archive)";
    existing.NumRowsInListing = 50;
    existing.DefaultSortColumn = "Date";
    existing.DefaultSortDirection = "desc";

    // Keep the existing ContentViewID so this is treated as an update
    var updated = await client.containerMethods.SaveContainer(existing, guid);
    Console.WriteLine($"Updated container: {updated?.ReferenceName}");
}

Read the container first and mutate what you got back. Constructing a partial object and saving it can blank out fields you didn't set.

Point a container at a different model

const newModel = await apiClient.modelMethods.getModelByReferenceName('Article', guid);

existing.contentDefinitionID = newModel.id;

await apiClient.containerMethods.saveContainer(existing, guid, false);
var newModel = await client.modelMethods.GetModelByReferenceName("Article", guid);

existing.ContentDefinitionID = newModel.ID;

await client.containerMethods.SaveContainer(existing, guid);

Repointing a container at a model with different fields leaves existing items holding values the new model doesn't define. Check the field overlap first.

Deleting containers

Delete a container

Delete by container ID.

await apiClient.containerMethods.deleteContainer(contentViewID, guid);
console.log('Container deleted successfully');
var result = await client.containerMethods.DeleteContainer(contentViewID, guid);
Console.WriteLine($"Deleted: {result}");

.NET signature: Task<string?> DeleteContainer(int? id, string guid)

Delete only when the container is empty

Check the container's content list before deleting so you don't remove a container that still holds items.

async function safeDeleteContainer(containerName: string, guid: string, locale: string) {
  const container = await apiClient.containerMethods.getContainerByReferenceName(
    containerName,
    guid
  );

  if (!container) {
    console.log('Container not found');
    return false;
  }

  const contentList = await apiClient.contentMethods.getContentList(
    containerName,
    guid,
    locale,
    { take: 1, skip: 0 }
  );

  if (contentList.totalCount > 0) {
    console.warn(`Cannot delete container - contains ${contentList.totalCount} content items`);
    return false;
  }

  await apiClient.containerMethods.deleteContainer(container.contentViewID, guid);
  return true;
}

getContentList is JavaScript only. From .NET, use GetContentItems to check for items before deleting.

Naming reference names

Reference names are the identifier every API call uses, so they're worth getting right the first time — renaming one breaks every query that used it.

Avoid hyphens. This is the one that bites hardest, and it isn't obvious. A container named Blog-Posts works fine in the CMS and through the Management API, but the GraphQL API derives its field names from the reference name, and a hyphenated container ends up unqueryable there — the content simply doesn't appear. We hit this on this very documentation site: a container named ManagementSDK-Articles returned its items over REST and zero items over GraphQL, with no error to explain why. Renaming it to ManagementSDKArticles fixed it.

Beyond that:

  • Use letters and numbers only. BlogPosts, FeaturedPosts, ProductCatalog.
  • Mixed case is fine, and common — Agility's own containers use PascalCase. Just be aware that the Fetch API lowercases reference names on read, so don't rely on casing to tell two containers apart.
  • Name by purpose, not position: FeaturedPosts, not Container1.
  • Keep the container name close to its model name so the relationship is obvious.
function validateReferenceName(name: string): string[] {
  const errors: string[] = [];

  if (!name) {
    errors.push('Reference name is required');
  }
  if (name.includes('-')) {
    errors.push('Hyphens break GraphQL queries — use letters and numbers only');
  }
  if (name && !/^[A-Za-z][A-Za-z0-9]*$/.test(name)) {
    errors.push('Must start with a letter and contain only letters and numbers');
  }

  return errors;
}

Patterns

Create a container only if it doesn't already exist

Look up the model, check for an existing container by reference name, and create it only when missing. This makes the operation safe to re-run.

async function createContainerForModel(
  modelReferenceName: string,
  containerReferenceName: string,
  guid: string
) {
  const model = await apiClient.modelMethods.getModelByReferenceName(
    modelReferenceName,
    guid
  );

  if (!model) {
    throw new Error(`Model '${modelReferenceName}' not found`);
  }

  const existing = await apiClient.containerMethods.getContainerByReferenceName(
    containerReferenceName,
    guid
  );

  if (existing) {
    console.log('Container already exists:', existing.referenceName);
    return existing;
  }

  return apiClient.containerMethods.saveContainer(
    {
      contentViewID: -1,
      referenceName: containerReferenceName,
      contentViewName: containerReferenceName,
      title: containerReferenceName,
      contentDefinitionID: model.id,
      contentDefinitionTypeID: 1
    },
    guid,
    true // force the reference name we asked for
  );
}

Create containers in bulk

Run the create-if-missing helper over a list of container/model pairs, collecting per-item results instead of failing the whole batch.

async function createMultipleContainers(
  configs: Array<{ containerName: string; modelName: string }>,
  guid: string
) {
  const results = [];

  for (const config of configs) {
    try {
      const container = await createContainerForModel(
        config.modelName,
        config.containerName,
        guid
      );
      results.push({ success: true, ...config, containerID: container.contentViewID });
    } catch (error) {
      results.push({ success: false, ...config, error: (error as Error).message });
    }
  }

  return results;
}

const results = await createMultipleContainers(
  [
    { containerName: 'BlogPosts', modelName: 'BlogPost' },
    { containerName: 'StaticPages', modelName: 'StaticPage' },
    { containerName: 'Products', modelName: 'ProductCatalog' }
  ],
  guid
);

Error handling

try {
  const container = await apiClient.containerMethods.getContainerByReferenceName('BlogPosts', guid);
} catch (error) {
  console.error('Error retrieving container:', error);
}
try
{
    var container = await client.containerMethods.GetContainerByReferenceName("BlogPosts", guid);
}
catch (ApplicationException ex)
{
    Console.Error.WriteLine($"Error: {ex.Message}");
}
On this page
Method referenceReading containersCreating and updating containersDeleting containersNaming reference namesPatternsError handling
Agility CMS documentationAgility CMS documentation

Documentation for the CMS built for editors, developers, and AI agents.

Docs
  • Overview
  • Editors
  • Developers
  • Owners & Admins
  • Training Guide
  • Changelog
Resources
  • Get Support
  • MCP Server
  • System Status
  • llms.txt
Agility
  • agilitycms.com
  • Start Free Trial
  • Sign in
  • Blog
© 2026 Agility Inc. All rights reserved.
Privacy PolicyTerms of Service