ACC Javascript SDK

Usage no npm install needed!

<script type="module">
  import adobeAccJsSdk from 'https://cdn.skypack.dev/@adobe/acc-js-sdk';


Adobe Campaign Classic (ACC) SDK in JavaScript (node.js and browser)

This is a node.js SDK for Campaign API. It exposes the Campaign API exactly like it is used inside Campaign using the NLWS notation.


Version 0.1.22


  • Update node-notifier library (used by jest) to version 8.0.1 to fix a possible injection

Version 0.1.20

Add client.hasPackage function to test if a package is installed or an instance or not (https://github.com/adobe/acc-js-sdk/issues/5)

Version 0.1.3

Bug fixes

Version 0.1.2

  • Use github action to automatically publish to npm when one pushes a commit with the message "Release 1.2.3"

Version 0.1.1

Bug fixes

Version 0.1.0

Initial version

API Basics

In order to call the API, you need to create a Client object

const sdk = require('./src/index.js');

const client = await sdk.init("https://myInstance.campaign.adobe.com", "admin", "admin");
const NLWS = client.NLWS;

The NLWS object allows to dynamically perform SOAP calls on the targetted Campaign instance.

You can get version information about the SDK


LogOn / LogOff

    await client.logon();
    await client.logoff();

Calling static APIs

Static APIs are the easiest to call. Once you have a NLWS object and logon to the server, call a static API as followed

    result = await NLWS.xtkSession.getServerTime();


  • xtkSession is made of the namespace and entity to which the API applies. For instance xtk:session -> xtkSession
  • getServerTime is the method name. In ACC, method names start with an upper case letter, but in JS SDK you can put it in lower case too.

Parameter types

In Campaign, many method attributes are XML elements or documents, as well as many return types. It's not very easy to use in JavaScript, so the SDK supports automatic XML <=> JSON conversion. Of yourse, you can still use XML if you want. We're using Badgerfish convention (http://www.sklar.com/badgerfish/) for the translation.

By default, JSON is used, but the representation can be changed as follow:

client.representation = "xml";
client.representation = "json";

Here's an example of a queryDef in JSON

    const queryDef = {
        "@schema": "nms:extAccount",
        "@operation": "select",
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@name" }

Returning multiple values

Campaign API can return one or multiple values. The SDK uses the following convention:

  • no return value -> returns `null``
  • one return value -> returns the value directly
  • more that one return value -> returns an array of values

Calling non-static APIs

To call a non-static API, you need an object to call the API on. You create an object with the create method. For instance, here's how one creates a QueryDef object.

    const queryDef = {
        "@schema": "nms:extAccount",
        "@operation": "select",
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@name" }
    const query = NLWS.xtkQueryDef.create(queryDef);

The method can then be called directly on the object

    const extAccounts = await query.executeQuery();

In this example, the result is as follows


Campaign data types

Campaign uses a typed system with some specificities:

  • for strings, "", null, or undefined are equivalent
  • numerical values cannot be null or undefined (0 is used instead)
  • boolean values cannot be null or undefined (false is used instead)
  • conversion between types is automatic based on their ISO representation
Xtk type JS type Comment
string 6 string never null, defaults to ""
memo 12 string
CDATA 13 string
byte 1 number signed integer in the [-128, 128[ range. Never null, defaults to 0
short 2 number signed 16 bits integer in the [-32768, 32768[ range. Never null, defaults to 0
long 3 number signed 32 bits integer. Never null, defaults to 0
float 4 number single-percision numeric value. Never null, defaults to 0
double 5 number single-percision numeric value. Never null, defaults to 0
datetime 7 Date UTC timestamp with second precision. Can be null
date 10 Date UTC timestamp with day precision. Can be null
boolean 15 boolean boolean value, defaultint to false. Cannot be null

The SDK user does not have to handle this, but outside of the Campaign ecosystem, those rules may not apply and you probably do not want to use a number for a string, etc. The XtkCaster class is here to help.

You get a static XtkCaster object like this

const XtkCaster = sdk.XtkCaster;

To convert a Campaign value into a given type, use one of the following.

stringValue = XtkCaster.asString(anyValue);
booleanValue = XtkCaster.asBoolean(anyValue);
byteValue = XtkCaster.asByte(anyValue);
shortValue = XtkCaster.asShort(anyValue);
int32Value = XtkCaster.asLong(anyValue);
numberValue = XtkCaster.asNumber(anyValue);
timestampValue = XtkCaster.asTimestamp(anyValue);
dateValue = XtkCaster.asDate(anyValue);

More dynamic conversions can be achieved using the as function. See the types table above for details.

stringValue = XtkCaster.as(anyValue, 6);

DOM helpers

DOM manipulation is sometimes a bit painful. The DomUtil helper provides a few convenience functions

const DomUtil = sdk.DomUtil;

Create DOM from XML string:

    const doc = DomUtil.parse("<root><one/></root>");

Writes a DOM document or element as a string:

    const s = DomUtil.toXMLString(docOrElement);

Creates a new document

    const queryDoc = DomUtil.newDocument("queryDef");

Escape text value

    const escaped = DomUtil.escapeXmlString(value);

Find element by name (finds the first)

    const el = DomUtil.findElement(parentElement, elementName, shouldThrow);

Get the text value of an elemenbt

    const text = DomUtil.elementValue(element);

Iterates over child elements

    var child = DomUtil.getFirstChildElement(parentElement);
    while (child) {
        child = DomUtil.getNextSiblingElement(child);

Iterates over child elements of a given type

    var methodChild = DomUtil.getFirstChildElement(parentElement, "method");
    while (methodChild) {
        methodChild = DomUtil.getNextSiblingElement(methodChild, "method");

Get typed attribute values

    const stringValue = DomUtil.getAttributeAsString(element, attributeName)
    const byteValue = DomUtil.getAttributeAsByte(element, attributeName)
    const booleanValue = DomUtil.getAttributeAsBoolean(element, attributeName)
    const shortValue = DomUtil.getAttributeAsShort(element, attributeName)
    const longValue = DomUtil.getAttributeAsLong(element, attributeName)

JSON to XML conversion (badger fish)

    const document = DomUtil.fromJSON(json);
    const json = DomUtil.toJSON(documentOrElement);


The following caches are manage by the SDK

  • Options cache. Stores typed option values, by option name.
  • Entity cache. Caches schemas and other entities
  • Method cache. Cahces SOAP method definitions.

Caches can be cleared at any time





External account passwords can be decrypted using a Cipher.

    const cipher = await client.getSecretKeyCipher();
    const password = cipher.decryptPassword(encryptedPassword);

Core API

Get option value

A convenience function is provided, which returns a typed option value.

    var value = await client.getOption("XtkDatabaseId");

Options are cached because they are often used. It's possible to force the reload of an option:

    var value = await client.getOption("XtkDatabaseId", false);

It's also possible to call the API directly. Use the xtk:session:GetOption method to return an option value and it's type. This call will not use the option cache for returning the option value, but will still cache the result.

    const optionValueAndType = await NLWS.xtkSession.getOption("XtkDatabaseId");
    console.log("Marketing datbaseId: " + optionValueAndType);

    Marketing datbaseId: u7F00010100B52BDE,6

If the option does not exist, it will return [ "", 0 ]

    var datbaseId = await client.getOption("XtkDatabaseId");

The cache can be cleared


Test if a package exists

  • Since: 0.1.20
  • Test if a package is installed. Expects to be connected to an instance
    var hasAmp = client.hasPackage("nms:amp");


    var hasAmp = client.hasPackage("nms", "amp");

Connect to mid-sourcing

From a marketing client connection, one can get a client to a mid server

    console.log("Connecting to mid server...");
    const midClient = await client.getMidClient();
    await midClient.client.logon();
    const datbaseId = await midClient.getOption("XtkDatabaseId");
    console.log("Mid datbaseId: " + datbaseId);
    await midClient.NLWS.xtkSession.testCnx();
    console.log("Disconnecting from mid");
    await midClient.client.logoff();


Tracking all SOAP calls

SOAP calls can be logged by setting the traceSOAPCalls attribute on the client at any time.

client.traceSOAPCalls = true;

This is an example of the logs

SOAP//request xtk:session#GetOption <SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://xml.apache.org/xml-soap"><SOAP-ENV:Header><Cookie>__sessiontoken=___3033D619-6710-450D-9194-CEB718D9F57B</Cookie><X-Security-Token>@p4IgqV7etc_liq15zAq59iYRLcCh6_tQkRC5WHbhIA8RTHkFt6VIc9R9RYA4NPwFcqtGh9-LmvrdplXgiiLWNA==</X-Security-Token></SOAP-ENV:Header><SOAP-ENV:Body><m:GetOption xmlns:m="urn:xtk:session" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><sessiontoken xsi:type="xsd:string">___3033D619-6710-450D-9194-CEB718D9F57B</sessiontoken><name xsi:type="xsd:string">XtkDatabaseId</name></m:GetOption></SOAP-ENV:Body></SOAP-ENV:Envelope>

SOAP//response xtk:session#GetOption <?xml version='1.0'?><SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Body><GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><pstrValue xsi:type='xsd:string'>uFE80000000000000F1FA913DD7CC7C4804BA419F</pstrValue><pbtType xsi:type='xsd:byte'>6</pbtType></GetOptionResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

Query API

List all accounts

    const queryDef = {
        "@schema": "nms:extAccount",
        "@operation": "select",
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@name" }
    const query = NLWS.xtkQueryDef.create(queryDef);

    const extAccounts = await query.executeQuery();

Get a single record

    var queryDef = {
        "@schema": "nms:extAccount",
        "@operation": "get",
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@name" },
                { "@expr": "@label" },
                { "@expr": "@type" },
                { "@expr": "@account" },
                { "@expr": "@password" },
                { "@expr": "@server" },
                { "@expr": "@provider" },
        "where": {
            "condition": [
                { "@expr": "@name='ffda'" }
    const query = NLWS.xtkQueryDef.create(queryDef);
    const extAccount = await query.executeQuery();


Results can be retrieved in different pages, using the @lineCount and @startLine attributes. For instance, retrieves profiles 3 and 4 (skip 1 and 2)

    var queryDef = {
        "@schema": "nms:recipient",
        "@operation": "select",
        "@lineCount": 2,
        "@startLine": 2,
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@email" }
    var query = NLWS.xtkQueryDef.create(queryDef);
    var recipients = await query.executeQuery();

Writer API

Creates an image (data is base64 encoded)

var data = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA9ElEQVQ4jaXTIUsFQRSG4eeKiBjEIBeDYDGoSUwGm81s8SdYtIhFhPMDbEaz/SIIZkGbWg1Gg0GwiIgYZPZuWBxn8bJvWXb2O+/scM70lAhjuMO1sF9IVaES61jFnjBbyLQKjurnJz6yr62CsI2t+m0gRhGERZw1Vk6zTFEQ+rjETOP3b7OqBr1G8SRusPYrc4I3LGCeapN37AqP443g8R/FiYNsZcgGSRCmq1ZxmEXa6Yt0hKh6/dAaLbOcd+H/XOGpi2AFU10EqWsTXQQ7wmsSPNdzP8DXCII0D41BSgxvXboHm1jCXDpnPbHfeME9znEh+AFoTyfEnWJgLQAAAABJRU5ErkJggg==";
var doc = {
    "@xtkschema": "xtk:image",
    "@_operation": "insert",
    "@namespace": "cus",
    "@name": "test.png",
    "@label": "Self test",
    "@type": "png",
    "data": { "quot;: data }
await NLWS.xtkPersist.write(doc);

Creates a folder (with image previously created)

const folder = {
    "@xtkschema": "xtk:folder",
    "@_operation": "insert",
    "@parent-id": 1167,
    "@name": "testSDK",
    "@label": "Test SDK",
    "@entity": "xtk:folder",
    "@schema": "xtk:folder",
    "@model": "xtkFolder",
    "@image-namespace": "cus",
    "@image-name": "test.png"
await NLWS.xtkPersist.write(folder);

Workflow API

Start and stop wotkflows, passing either an id or workflow internal name

    await NLWS.xtkWorkflow.stop(4900);
    await NLWS.xtkWorkflow.start(4900);

A workflow can be started with parameters. Variables, are passed as attributes of the parameters document.

await NLWS.xtkWorkflow.startWithParameters(4900, { "@hello": "world" });

The variables can be used in the workflow as attributes of the instance.vars variable.


Profiles and subscriptions

Create a recipient

    var recipient = {
        "@xtkschema": "nms:recipient",
        "@_operation": "insert",
        "@firstName": "Thomas",
        "@lastName": "Jordy",
        "@email": "jordy@adobe.com"
    await NLWS.xtkPersist.write(recipient);

Create multiple recipients

    var recipients = {
        "@xtkschema": "nms:recipient",
        "recipient": [
                "@_operation": "insert",
                "@firstName": "Christophe",
                "@lastName": "Protat",
                "@email": "protat@adobe.com"
                "@_operation": "insert",
                "@firstName": "Eric",
                "@lastName": "Perrin",
                "@email": "perrin@adobe.com"
    await NLWS.xtkPersist.writeCollection(recipients);

List all recipients in Adobe

    var queryDef = {
        "@schema": "nms:recipient",
        "@operation": "select",
        "select": {
            "node": [
                { "@expr": "@id" },
                { "@expr": "@firstName" },
                { "@expr": "@lastName" },
                { "@expr": "@email" }
        "where": {
            "condition": [
                { "@expr": "GetEmailDomain(@email)='adobe.com'" }
    const query = NLWS.xtkQueryDef.create(queryDef);
    var recipients = await query.executeQuery();

Count total number of profiles

    var queryDef = {
        "@schema": "nms:recipient",
        "@operation": "count"
    var query = NLWS.xtkQueryDef.create(queryDef);
    var count = await query.executeQuery();
    count = XtkCaster.asLong(count["@count"]);

Update a profile. In this case, use the "@email" attribute as a key. If the @_key attribute is not specified, the primary key will be used.

    var recipient = {
        "@xtkschema": "nms:recipient",
        "@_key": "@email",
        "@_operation": "update",
        "@firstName": "Alexandre",
        "@email": "amorin@adobe.com"
    await NLWS.xtkPersist.write(recipient);

Deletes a profile

    var recipient = {
        "@xtkschema": "nms:recipient",
        "@_key": "@email",
        "@_operation": "delete",
        "@email": "amorin@adobe.com"
    await NLWS.xtkPersist.write(recipient);

Deletes a set of profiles, based on condition. For instance delete everyone having an email address in adobe.com domain

    await NLWS.xtkPersist.deleteCollection("nms:recipient", { condition: { "@expr": "GetEmailDomain(@email)='adobe.com'"} });


One can register a recipient to a service


Reading schemas is a common operation in Campaign. The SDK provides a convenient function as well as caching for efficient use of schemas.

    const schema = await client.getSchema("nms:recipient");

A given representation can be forced

    const xmlSchema = await client.getSchema("nms:recipient", "xml");
    const jsonSchema = await client.getSchema("nms:recipient", "json");

System enumerations can also be retreived with the fully qualified enumeration name

    const sysEnum = await client.getSysEnum("nms:extAccount:encryptionType");

or from a schema

    const schema = await client.getSchema("nms:extAccount");
    const sysEnum = await client.getSysEnum("encryptionType", schema);

Get a source schema

    var srcSchema = await NLWS.xtkPersist.getEntityIfMoreRecent("xtk:srcSchema|nms:recipient", "", false);

Build & Run

To build this project, you need node and npm

npm install

Deploy to npm

To deploy to npm

  • Build and run tests locally
  • Increase version in package.json
  • Push a commit with message Release 1.2.3

Client-side SDK

The SDK can also be used client side.

Compile the client-side SDK

Go to the root folder of the SDK and compile the SDK

node compile.js

It generates a file named bundle.js

ACC client-side SDK compiler version 0.1.0
Bundling ../package.json
Bundling ./web/jsdom.js
Bundling ./web/crypto.js
Bundling ./web/request.js
Bundling ./xtkCaster.js
Bundling ./dom.js
Bundling ./xtkEntityCache.js
Bundling ./methodCache.js
Bundling ./optionCache.js
Bundling ./soap.js
Bundling ./crypto.js
Bundling ./client.js
Bundling ./index.js
Client-side SDK generated in ./bundle.js

Deploy the SDK

Once compiled, copy it to the Campaign server (here on a dev environment).

cd /c/cygwin64/home/neolane/ac
cp "Z:\amorin On My Mac\Documents\dev\git\ac7\acc-js-sdk\bundle.js" nl/web/accSDK.js
cp "Z:\amorin On My Mac\Documents\dev\git\ac7\acc-js-sdk\index.html" nl/web/index.html

This makes them available on



Include the SDK

<script src="accSDK.js"></script>

Use the SDK


    (async () => {

        const client = await accSDK.init("http://ffdamid:8080", "admin", "admin");
        await client.logon();
        var databaseId = await client.getOption("XtkDatabaseId");
        document.getElementById("hello").textContent = databaseId;
        await client.logoff();



Contributions are welcomed! Read the Contributing Guide for more information.


This project is licensed under the Apache V2 License. See LICENSE for more information.