Agility CMS documentationAgility CMS documentation
OverviewEditorsDevelopersOwners & AdminsTraining GuideApps
Sign inLet's Chat
Management SDK
Getting Started

Introduction

Getting Started with the Management SDK

Install and authenticate the Agility Management SDK in JavaScript or .NET — OAuth, Personal Access Tokens, client setup, and regional endpoints.

Managing content from outside the CMS has never been easier. The Agility Management SDK lets you create and update content, apply workflow actions, and manage pages, models, containers, and assets through the Agility Management API.

The SDK ships for JavaScript/TypeScript and .NET. This section documents both — use the tabs in each code sample to switch languages, and your choice is remembered as you read.

Use cases

  • Importing content from external systems
  • Implementing custom approval workflows
  • Keeping content in sync with other platforms
  • Bulk updating or publishing content lists
  • Managing pages programmatically

Installation

npm install @agility/management-sdk
dotnet add package Agility.Management.SDK --prerelease

Two things will trip you up on the .NET side. The assembly is named management.api.sdk.dll, but the NuGet package id is Agility.Management.SDK — asking for management.api.sdk fails with a package-not-found error. And every published version is still a prerelease, so without --prerelease (or an explicit --version) NuGet reports that no installable version exists. Treat the .NET SDK as beta accordingly.

Both SDKs are open source:

  • JavaScript/TypeScript — agility-cms-management-sdk-typescript
  • .NET — agility-cms-management-sdk-dotnet (built on .NET 6+ / RestSharp)

Authentication

Every request needs an access token. The SDK supports two ways to get one: OAuth 2.0 (for interactive apps) and Personal Access Tokens (for automation — see below).

Step 1 — Start the authorization flow

Send the user to the authorize endpoint. Add the offline_access scope if you want a refresh token so the integration can run unattended:

GET https://mgmt.aglty.io/oauth/authorize
  ?response_type=code
  &redirect_uri=YOUR_REDIRECT_URI
  &state=YOUR_STATE
  &scope=openid profile email offline_access

Step 2 — Exchange the code for a token

Your redirect URI receives an authorization code. Exchange it for an access token:

const response = await fetch("https://mgmt.aglty.io/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({ code: "YOUR_AUTHORIZATION_CODE" }),
})

const { access_token, refresh_token, expires_in } = await response.json()
// POST https://mgmt.aglty.io/oauth/token
// Content-Type: application/x-www-form-urlencoded
// code=YOUR_AUTHORIZATION_CODE
using var http = new HttpClient();
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["code"] = "YOUR_AUTHORIZATION_CODE"
});

var response = await http.PostAsync("https://mgmt.aglty.io/oauth/token", body);
var json = await response.Content.ReadAsStringAsync();

The response is the same shape either way: access_token, token_type, expires_in, and — when you asked for offline_access — refresh_token.

Step 3 — Refresh an expired token

POST https://mgmt.aglty.io/oauth/refresh?refresh_token=YOUR_REFRESH_TOKEN

Token lifetimes. Access tokens last 24 hours — after that, requests return 401 Unauthorized. Refresh tokens last 30 days, and only exist if you authorized with the offline_access scope. Store both securely and never expose them in client-side code.


Setting up the client

With a token in hand, initialize the client and make your first request:

import * as mgmtApi from "@agility/management-sdk"

// Initialize the Options class
const options = new mgmtApi.Options()
options.token = "<<your-access-token>>"

// Initialize the ApiClient
const apiClient = new mgmtApi.ApiClient(options)

const guid = "<<your-instance-guid>>"
const locale = "en-us"

// Get the content item with ID 22
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale)
console.log(JSON.stringify(contentItem))
using management.api.sdk;
using agility.models;

// Initialize Options with your access token
var options = new Options
{
    token = "<<your-access-token>>"
};

// Create the client instance
var client = new ClientInstance(options);

var guid = "<<your-instance-guid>>";
var locale = "en-us";

// Get the content item with ID 22
var contentItem = await client.contentMethods.GetContentItem(22, guid, locale);
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(contentItem));

Note that the .NET using statement is management.api.sdk — the assembly name — even though you installed the Agility.Management.SDK package.

Options fields

FieldPurpose
tokenOAuth access token or PAT (required)
baseUrlOverride the API base URL (optional)
refresh_tokenOAuth refresh token (optional)
durationRetry polling interval in ms (default 3000)
retryCountMax retry attempts for batch polling (default 500)

These are SDK-side settings, not API parameters — you won't find them in the Management API spec. duration and retryCount control the batch polling described in How writes complete below.

Regional endpoints

The SDK picks the right API host from your instance GUID's suffix — you normally don't set baseUrl yourself:

GUID suffixRegionEndpoint
-uUShttps://mgmt.aglty.io
-us2US 2https://mgmt-usa2.aglty.io
-cCanadahttps://mgmt-ca.aglty.io
-eEuropehttps://mgmt-eu.aglty.io
-aAustraliahttps://mgmt-aus.aglty.io
-dDevhttps://mgmt-dev.aglty.io

Each host serves its own Swagger UI — append /swagger to try endpoints interactively against your own instance.


Personal Access Tokens (PAT)

For automation, CI/CD pipelines, and server-side jobs where an OAuth redirect isn't practical, use a Personal Access Token.

Generating a PAT

PATs are created through the Management API. Authenticate with OAuth first, then call POST /api/v1/tokens/create — via your region's Swagger UI (append /swagger to the endpoints in the table above), with this body:

{
  "name": "my-automation-token",
  "expiryDate": "2028-01-01T00:00:00Z"
}

name is the only required field. expiryDate is optional in the API spec, and the spec doesn't define what happens when you omit it — so always set an explicit future date rather than relying on unspecified behaviour.

A successful call returns 201 with the token in the response's token field. That value is returned only once — save it immediately, and note that the notice field carries an accompanying message about it. Alongside the usual 400, 401, and 403 errors, 429 is a documented response, so automation that creates tokens in bulk should back off and retry.

Response fieldWhat it tells you
tokenIDIdentifier for the token — used by the token management endpoints
nameThe name you supplied
tokenThe secret value, returned only on creation
noticeMessage accompanying the newly created token
expiryDateWhen the token expires
createdDateWhen the token was created
enabledWhether the token is currently active
isExpiredtrue once the expiry date has passed
daysUntilExpirationDays of life left — useful for rotating tokens before they lapse
lastUsedDateLast time the token authenticated a request

Companion endpoints let you audit and rotate tokens: GET /api/v1/tokens/list, GET /api/v1/tokens/{tokenId}, PUT /api/v1/tokens/{tokenId}/update, and DELETE /api/v1/tokens/{tokenId}/delete.

Using a PAT

Initialization is identical to OAuth — pass the PAT as token:

import * as mgmtApi from "@agility/management-sdk"

const options = new mgmtApi.Options()
options.token = "<<your-personal-access-token>>"

const apiClient = new mgmtApi.ApiClient(options)
var options = new Options
{
    token = "<<your-personal-access-token>>"
};

var client = new ClientInstance(options);

PAT restrictions: PATs cover content operations but cannot touch user management, token management, or admin-level endpoints — those return 403 Forbidden.


How writes complete

Two things about writing through this API surprise people. Both are worth knowing before your first save, because each one looks like a bug when you hit it.

Every write is queued, and returns a batch ID

The Management API does not perform a write during your request. It queues a batch and responds with a batch ID — an integer that identifies the queued work, not the content ID and not a result. The actual save happens moments later.

The SDK hides this for you. saveContentItem, publishContent and friends take the batch ID, poll until the batch completes, and return the real result — which is why Options has duration and retryCount: they are the polling interval and the attempt ceiling. If a large import times out waiting, raise retryCount rather than assuming the write failed.

If you want the batch ID instead of waiting, pass returnBatchId: true (JavaScript only). You are then responsible for polling it yourself:

GET /api/v1/instance/{guid}/batch/{batchID}

That URL is not locale-scoped — /{guid}/batch/{id}, with no /{locale} segment. The batch is finished when batchState is 3. A freshly created batch ID can return 404 for a moment before it exists, so treat an early 404 as "not yet", not as failure.

Calling the REST API directly? Then this is your problem, not the SDK's. POST /item gives you a batch ID and nothing else; read the item straight back and you will get the old value, which looks exactly like a write that silently did nothing. Poll the batch before you trust the result.

A save lands in Staging, not on your live site

Saving does not publish. A save always writes to Staging, and if the item was already Published it drops back to a Staging state — your live site keeps serving the previous version until you publish explicitly.

So changing one field on a live item is always two operations:

// 1. save — the change now exists, in Staging
const contentID = await apiClient.contentMethods.saveContentItem(item, guid, locale)

// 2. publish — only now is it live
await apiClient.contentMethods.publishContent(contentID, guid, locale)

Skip the second step and the change is real, stored, and invisible to your site — the single most common reason a write "didn't work". The same applies to pages, and to every bulk equivalent: see Content Items and Pages.


SDK operations

The client exposes method groups for each area of the CMS. The JavaScript client has ten; .NET has the first seven.

Method groupCoversIn .NET?
contentMethodsContent items — CRUD, workflow, publishingYes
containerMethodsContainers (content lists)Yes
modelMethodsContent & component modelsYes
pageMethodsPages and page templatesYes
assetMethodsMedia — upload and manage assetsYes
instanceUserMethodsInstance users and permissionsYes
batchMethodsBatch status, and batch-level workflow actionsPartly — status only
instanceMethodsLocales and Fetch API sync statusNo
serverUserMethodsThe authenticated userNo
webhookMethodsWebhook CRUDNo

Feature differences between the SDKs

The .NET SDK trails the JavaScript SDK, and it's worth being precise about what that means: every gap below is an SDK gap, not an API gap. The endpoint exists in all cases, so from .NET you can call the Management API directly and lose nothing but the wrapper.

JavaScript-onlyCall this from .NET instead
instanceMethods.getLocales()GET /api/v1/instance/{guid}/locales
instanceMethods.getFetchApiStatus()GET /api/v1/instance/{guid}/fetch-api-status?mode=fetch
serverUserMethods.me()GET /api/v1/users/me
webhookMethods (webhook CRUD)GET/POST /api/v1/instance/{guid}/webhook, GET/DELETE .../webhook/{id}
contentMethods.batchWorkflowContent()POST /api/v1/instance/{guid}/{locale}/item/batch-workflow
contentMethods.getContentList() (POST filtering)POST /api/v1/instance/{guid}/{locale}/list/{referenceName}
contentMethods.getContentHistory()GET /api/v1/instance/{guid}/{locale}/item/{contentID}/history
contentMethods.getContentComments()GET /api/v1/instance/{guid}/{locale}/item/{contentID}/comments
containerMethods.getContainerListPaged()GET /api/v1/instance/{guid}/container/list/paged
pageMethods.getPageHistory()GET /api/v1/instance/{guid}/{locale}/page/{id}/history
pageMethods.getPageComments()GET /api/v1/instance/{guid}/{locale}/page/{id}/comments
pageMethods.batchWorkflowPages()POST /api/v1/instance/{guid}/{locale}/page/batch-workflow
batchMethods.publishBatch() and the other batch workflow actionsPOST /api/v1/instance/{guid}/batch/{id}/publish (and /unpublish, /approve, /decline, /request-approval)
assetMethods.deleteFolder(), assetMethods.renameFolder()The corresponding asset endpoints

If you'd rather not hand-roll HTTP calls, the JavaScript SDK is the fuller surface today.

Keeping this list honest. These tables are generated from the published packages — @agility/management-sdk on npm and Agility.Management.SDK on NuGet — rather than written from memory. If you spot a difference, the packages win; please tell us so we can correct the page.

On this page
Use casesInstallationAuthenticationSetting up the clientPersonal Access Tokens (PAT)How writes completeSDK operationsFeature differences between the SDKs
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