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 aBearer
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 aBasic
authentication string, which is a base64-encoded string containingusername:password
.
Example:Authorization: Basic am9obi5zbWl0aDpqb2huMTIz
- Request parameters
u
andp
for username and password, orapiKey
for the user's API Key.
Examples:u=john.smith&p=john123
ORapiKey=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"
}}
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
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
}
}
Search
/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"]
}
}
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": "Jan 12, 2023 12:00 AM",
"endDate": "Aug 12, 2023 11:59 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;