REST API

Cascade CMS REST API is a lightweight API for interfacing with Cascade's Web Services.

What is REST?

Formally, REST describes a way for a client to interact with web resources from a server using a set of stateless operations accessed via URIs.

Though the Cascade CMS v1 REST APi does not exactly follow RESTful API conventions, it is similar enough to be called so. The supported methods are "GET" and "POST". The name of the operation should be included in the URL. The request and response are in JSON format that resembles format of SOAP objects from Cascade CMS's Web Services.

Future version of the API may adhere more closely to REST.

Symbols in this document

{identifier}

Either:

  • End of URL string for POST or GET requests: {type}/{id}. Example: folder/2b2fd9bc7f0000010044b22e65131cd3
  • End of URL string for POST or GET requests: {type}/{siteName}/{path}.
    Example: page/www.example.com/news/2003/best-of-show
  • The identifier can also be provided in body in JSON format for POST requests only.
    Example:
    "identifier": {
     "type": "page",
     "path": {
       "siteId": "2b2fd9a67f0000010044b22e4f6b0859",
       "path": "news/2003/about"
    }
    }

Notice that providing a site id for the identifier is not possible in a URL string.

{auth}

Either:

  • The user's API Key can be provided within the request's Authorization header as a Bearer token.
    Example: Authorization: Bearer 27c03f58-7c79-45d1-aa8f-76d697bbb10d
  • The user's username and password can be provided within the request's Authorization header using a Basic authentication string, which is a base64-encoded string containing username:password.
    Example: Authorization: Basic am9obi5zbWl0aDpqb2huMTIz
  • Request parameters u and p for username and password, or apiKey for the user's API Key.
    Examples: u=john.smith&p=john123 OR apiKey=27c03f58-7c79-45d1-aa8f-76d697bbb10d
  • Authentication can also be provided in body in JSON format for POST requests only.
    Examples:
    "authentication": {
     "username": "john.smith",
     "password": "john123"
    }

    "authentication": {
     "apiKey": "27c03f58-7c79-45d1-aa8f-76d697bbb10d"
    }
{wsdl}

JSON structure defined in Cascade instance's WSDL file, which can be viewed by appending /ws/services/AssetOperationService?wsdl to Cascade's URL in the web browser. For example: http://localhost:8080/ws/services/AssetOperationService?wsdl.

To read the WSDL file for given parameter, first search for a <complexType> tag with given name parameter. For example, moveParameters definition in WSDL can be found by searching for <complexType name="moveParameters">. In that case, the result will be:

<sequence>
  <element maxOccurs="1" minOccurs="1" name="destinationContainerIdentifier" nillable="false" type="impl:identifier"/>
  <element maxOccurs="1" minOccurs="1" name="doWorkflow" nillable="false" type="xsd:boolean"/>
  <element maxOccurs="1" minOccurs="1" name="newName" nillable="false" type="xsd:string"/>
</sequence>
 

If type is of impl:..., it means that there is another <complextType> or <simpleType> tag with given name, for example <complexType name="identifier">. This has its own structure:

<sequence>
<!-- When editing and selected asset is recycled, it is recommended to preserve this relationship by providing selected asset's id
in case if the selected asset gets restored from the recycle bin.
One is REQUIRED -->
<element maxOccurs="1" minOccurs="0" name="id" type="xsd:string"/>
<!-- Path works only for non-recycled assets -->
<element maxOccurs="1" minOccurs="0" name="path" type="impl:path"/>
<element maxOccurs="1" minOccurs="1" name="type" type="impl:entityTypeString"/>
<!-- NOT REQUIRED: For reading purposes only. Ignored when editing, copying etc. -->
<element maxOccurs="1" minOccurs="0" name="recycled" type="xsd:boolean"/>
</sequence>
 

Value 0 in minOccurs means that the property is optional. As a result, an example JSON for moveParameters could look like this:

"moveParameters": {
 "destinationContainerIdentifier": {
   "id": "2b2fea0f7f0000010044b22e35685d26",
   "type": "folder"
 },
 "doWorkflow": "false",
 "newName": "index2"
}

or like this:

"moveParameters": {
 "destinationContainerIdentifier": {
   "path": {
     "path": "/news",
     "siteName": "www.example.com"
   },
   "type": "folder"
 },
 "doWorkflow": "false",
 "newName": "index2"
}}
Hint: The asset property is quite complex. An easy way to understand its structure is by simply performing a read operation on an existing asset in the system. If you would like to create an asset (a page for instance), it would be useful to read a similar asset first to see the proper structure of asset property. The same object can even be reused for create operation with just tweaked parameters. Similarly, the easiest way to edit an asset is by reading it first and reusing the read asset object.

Operations

Read

/api/v1/read/{identifier}?{auth}

example:

http://localhost:8080/api/v1/read/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

Delete

/api/v1/delete/{identifier}?{auth}

  • Include “deleteParameters” in message body based on {wsdl}
  • Optionally include “workflowConfiguration” in message body based on {wsdl}

example:

http://localhost:8080/api/v1/delete/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

Create

/api/v1/create?{auth}

  • Include “asset” in message body based on {wsdl}
http://localhost:8080/api/v1/create?u=hill&p=hill
{
 'asset': {
   'page': {
     'name': 'test',
     'parentFolderPath': '/',
     'siteName': 'www.example.com',
     'contentTypeId': 'b9bc37270a00016b00899e533ba18fe5',
     'xhtml': '<div>Content</div>',
     'metadata': {
       'title': 'Page title'
     }
   }
 }  
}

Edit

/api/v1/edit?{auth}

  • Include “asset” in message body based on {wsdl}

example:

http://localhost:8080/api/v1/edit?u=hill&p=hill 
{
 'asset': {
   'page': {
     'id': 'b9b0a96c0a00016b00899e5325baab29',
     'contentTypeId': 'b9bc37270a00016b00899e533ba18fe5',
     'xhtml': '<div>Content</div>',
     'metadata': {
       'title': 'Page title'
     }
   }
 }  
}

Copy

/api/v1/copy/{identifier}?{auth}

  • Include “copyParameters” in message body based on {wsdl}
  • Optionally include “workflowConfiguration” in message body based on {wsdl}

example:

http://localhost:8080/api/v1/copy/page/www.example.com/page-to-copy?u=hill&p=hill
{
 'copyParameters': {
   'newName': 'copied-page'
     "destinationContainerIdentifier": {
      'type': 'folder',
      'path': {
      'siteName': 'site-name',
        'path': '/'
    }
  }
}
}

Move

/api/v1/move/{identifier}?{auth}

  • Include “moveParameters” in message body based on {wsdl}
  • Optionally include “workflowConfiguration” in message body based on {wsdl}

Publish

/api/v1/publish/{identifier}?{auth}

  • Optionally include “publishInformation” in message body based on {wsdl}. Its “identifier” property is not necessary if it was provided in URL.

example:

http://localhost:8080/api/v1/publish/page/www.example.com/news/2003/best-of-show?u=hill&p=hill
Note: The response success: true in this case means that the asset has been successfully added to the publish queue. It does not indicate that the asset has actually been published yet to the corresponding Destinations in the request.
Tip: To unpublish, you can include the following in your POST request: 
{
"publishInformation": {
"unpublish": true
}
}

/api/v1/search?{auth}

  • Include “searchInformation” in message body based on {wsdl}

example:

http://localhost:8080/api/v1/search?u=hill&p=hill
{
  "searchInformation": {
    "searchTerms":"https://www.hannonhill.com",
    "searchFields": ["xml"],
    "searchTypes": ["page"]
  }
}
Note: The searchFields and searchTypes parameters must be included as arrays as seen above.

ReadAccessRights

/api/v1/readAccessRights/{identifier}?{auth}

example:

http://localhost:8080/api/v1/readAccessRights/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

EditAccessRights

/api/v1/editAccessRights/{identifier}?{auth}

  • Include “accessRightsInformation” in message body based on {wsdl}. Its “identifier” property is not necessary if it was provided in URL.

ReadWorkflowSettings

/api/v1/readWorkflowSettings/{identifier}?{auth}

example:

http://localhost:8080/api/v1/readWorkflowSettings/folder/www.example.com/news?u=hill&p=hill

EditWorkflowSettings

/api/v1/editWorkflowSettings/{identifier}?{auth}

  • Optionally include “workflowSettings” in message body based on {wsdl}. Its “identifier” property is not necessary if it was provided in URL. If “workflowSettings” is not provided, the folder will have all workflow definitions removed and workflow will not be required or inherited.
  • Optionally include “applyInheritWorkflowsToChildren” with “true” or “false” value (false by default)
  • Optionally include “applyRequireWorkflowToChildren” with “true” or “false” value (false by default)

example:

http://localhost:8080/api/v1/editWorkflowSettings/folder/www.example.com/news?u=hill&p=hill

ListSubscribers

/api/v1/listSubscribers/{identifier}?{auth}

example:

http://localhost:8080/api/v1/listSubscribers/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

ListMessages

/api/v1/listMessages?{auth}

example:

http://localhost:8080/api/v1/listMessages?u=hill&p=hill

MarkMessage

/api/v1/markMessage/{identifier}?{auth}

  • Include “markType” in message body with value “read” or “unread”

example:

http://localhost:8080/api/v1/markMessage/message/21903a3d7f000001554c369f276691eb?u=hill&p=hill
{
 'markType': 'read'
}

DeleteMessage

/api/v1/deleteMessage/{identifier}?{auth}

example:

http://localhost:8080/api/v1/deleteMessage/message/21903a3d7f000001554c369f276691eb?u=hill&p=hill

SendMessage (deprecated)

/api/v1/sendMessage?{auth}

  • Include “message” in message body based on {wsdl}

CheckOut

/api/v1/checkOut/{identifier}?{auth}

example:

http://localhost:8080/api/v1/checkOut/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

CheckIn

/api/v1/checkIn/{identifier}?{auth}

  • Optionally include string “comments” in message body

example:

http://localhost:8080/api/v1/checkIn/page/www.example.com/news/2003/best-of-show?u=hill&p=hill
{
 'comments': 'Here are some comments.'
}

SiteCopy

/api/v1/siteCopy?{auth}

  • Include string “originalSiteId” or “originalSiteName” in message body
  • Include string “newSiteName” in message Body

example:

http://localhost:8080/api/v1/siteCopy?u=hill&p=hill
{
 originalSiteName: 'www.example.com',
 newSiteName: 'new-site'
}

ListSites

/api/v1/listSites?{auth}

example:

http://localhost:8080/api/v1/listSites?u=hill&p=hill

ReadAudits

/api/v1/readAudits/{identifier}?{auth}

  • Optionally include “auditParameters” in message body based on {wsdl}. Its “identifier” property is not necessary if it was provided in URL.
  • Date behavior:
    • no dates: last week's audits, based on current date
    • start date only: all audits after start date
    • end date only: one week’s worth of audits, based on end date
    • start and end date: all audits between dates

example:

http://localhost:8080/api/v1/readAudits/user/hill?u=hill&p=hill
{
  "auditParameters": {
  "startDate": "May 12, 2023 12:00:00 AM",
  "endDate": "Aug 12, 2023 11:59:00 PM"
  }
}

ReadWorkflowInformation

/api/v1/readWorkflowInformation/{identifier}?{auth}

example:

http://localhost:8080/api/v1/readWorkflowInformation/page/www.example.com/news/2003/best-of-show?u=hill&p=hill

PerformWorkflowTransition

/api/v1/performWorkflowTransition?{auth}

  • Include “workflowTransitionInformation” in message body based on {wsdl}

ReadPreferences

/api/v1/readPreferences?{auth}

example:

http://localhost:8080/api/v1/readPreferences?u=hill&p=hill

EditPreference

/api/v1/editPreference?{auth}

  • Include “preference” in message body based on {wsdl}

example:

http://localhost:8080/api/v1/editPreference?u=hill&p=hill
{
 'preference': {
   'name': 'system_pref_global_area_external_link_check_on_publish',
   'value': 'on'
 }
}

Differences between SOAP and REST API in Cascade CMS

The REST API's request and response structure is quite similar to the existing SOAP web services' request and response structure. The obvious difference is that SOAP web services use XML to communicate while REST uses JSON. However, there are few more subtle differences:
  • When using REST API there is no need for any additional library to handle requests. As long as the specific language can handle JSON and sending requests through the network, that language can use REST API. This opens doors for easy usage of REST API in Javascript and .NET (.NET has a SOAP library but it is hard to set up and it runs into problems), while in PHP there is no need to enable the PHP SOAP module.
  • To use web services through SOAP with SSL (URLs with "https://"), additional settings are required in Apache to allow Cascade to connect to itself through SSL so that it can load the WSDL file. No such settings are necessary when using REST API, which reduces the burden on the server administrators.
  • There are a few differences between the XML SOAP envelope vs JSON. For instance, null values are returned in XML with xsi:nil="true" attribute while in JSON the null values are simply not there. Another difference is that arrays are wrapped in elements in XML, which then can be interpreted differently by different language specific libraries, while in REST API, the JSON response has plain arrays with elements in them.
  • Reading/Editing File assets using REST API uses byte array format whereas SOAP uses base64 encoded format.
  • SOAP accepts authentication only in the request body. Using REST API it is allowed to pass authentication to the URL. This is secure for the network over SSL - the credentials will be encrypted so that nobody can intercept the network connection and get the credentials. However, there is a chance that the server itself has logging enabled that stores accessed URLs. At that time, the server administrator could access the logs and see the password. To be 100% sure that the credentials cannot be seen by anyone (even the server administrator), you can pass credentials in the POST request's body. Even the "read" operation is allowed to be executed using POST request.
  • An average REST API operation has been reported to take about 30% longer time to execute. This might improve in the future.

Language specific examples

Below you can find example usage of the API.

Javascript (with jQuery)

This changes title of a page “news/2003/best-of-show” in site “example.com” by performing a “read” operation first, changing title and then performing “edit” operation:

$.get("http://localhost:8080/api/v1/read/page/example.com/news/2003/best-of-show?u=hill&p=hill", function(data) {
 if (data.success) {
   data.asset.page.metadata.title = 'New title';
   $.post("http://localhost:8080/api/v1/edit?u=hill&p=hill", JSON.stringify({
     'asset': data.asset
   }), function(data) {
     if (data.success)
       console.log('Success');
     else
       console.log('Error occurred when issuing an edit: ' + data.message);
   }, 'json');
 } else {
   console.log('Error occurred when issuing a read: ' + data.message);
 }
}, 'json');

Javascript (with fetch)

This is the same example as above but it does not require importing jQuery or any other libraries. It can be run even from browser's Developer Tools console.

fetch("http://localhost:8080/api/v1/read/page/example.com/news/2003/best-of-show", {
"headers": {
"Authorization": "Basic aGlsbDpoaWxs"
}
})
.then(r => r.json())
.then(data => {
if (data.success) {
data.asset.page.metadata.title = 'New title';
fetch("http://localhost:8080/api/v1/edit", {
method: 'POST',
headers: {
"Authorization": "Basic aGlsbDpoaWxs"
},
body: JSON.stringify({'asset': data.asset})
})
.then(r => r.json())
.then(data => {
if (data.success)
console.log('Success');
else
console.log('Error occurred when issuing an edit: ' + data.message);
});
} else {
console.log('Error occurred when issuing a read: ' + data.message);
}
});

Javascript (with fetch and async/await)

Again, this is the same example as above that does not require importing any libraries but it uses newer ES syntax, making the code a bit cleaner. It can be run from browser's Developer Tools console of a modern browser.

(async () => {
const readResult = await fetch("http://localhost:8080/api/v1/read/page/example.com/news/2003/best-of-show", {
headers: {
"Authorization": "Bearer your-api-key"
}
});
const readData = await readResult.json();
if (readData.success) {
readData.asset.page.metadata.title = 'New title';
const editResult = await fetch("http://localhost:8080/api/v1/edit", {
method: 'POST',
headers: {
"Authorization": "Bearer your-api-key"
},
body: JSON.stringify({asset: readData.asset})
});
const editData = await editResult.json();
if (editData.success)
console.log('Success');
else
console.log('Error occurred when issuing an edit: ' + editData.message);
} else {
console.log('Error occurred when issuing a read: ' + readData.message);
}
})();

Reading and parsing a File's byte array contents

(async (
  url = "http://localhost:8080/api/v1/read/file/example.com/image/sample.png"",
apiKey = "your-api-key"
) => {
const readResult = await fetch(url, {
headers: {
"Authorization": `Bearer ${apiKey}`
}
});
const readData = await readResult.json();
if (readData.success) {
const encoder = new TextEncoder();
const buffer = encoder.encode(readData.asset.file.text).buffer
const sliced = Array.prototype.slice.call(new Uint8Array(buffer), 0);
console.dir(sliced)
} else {
console.log('Error occurred when issuing a read: ' + readData.message);
}
})();

PHP

GET operations are very simple. For instance, this reads a role with id “1”:

$reply = json_decode(file_get_contents('http://localhost:8080/api/v1/read/role/1?u=admin&p=admin'));
print_r($reply);

POST operations are also simple with this utility function to be able to easily convert data between PHP array and JSON:

function apiOperation($url, $params)
{
   return json_decode(file_get_contents($url, false, stream_context_create(array('http' => array('method'  => 'POST','content' => json_encode($params))))));
}

Here is a PHP example similar to the Javascript example above:

$reply = json_decode(file_get_contents('http://localhost:8080/api/v1/read/page/example.com/news/2003/best-of-show?u=hill&p=hill'));

if ($reply->success)
{
   $reply->asset->page->metadata->title="A new title";
   $reply = apiOperation('http://localhost:8080/api/v1/edit?u=hill&p=hill', array ('asset' => $reply->asset));
   if ($reply->success)
       echo "Success.";
   else
       echo "Error occurred when issuing an edit: " . $reply->message;
}
else
   echo "Error occurred when issuing a read: " . $reply->message;