The NetSuite API: Exploring SOAP Requests

Overview

Since the invention of the relational database itself, Oracle has been a force in database and software development. Today, Oracle continues to provide best-in-class software and tooling.

Oracle's NetSuite is an example of one such leading application— an ERP/CRM used by many of Zuar's Runner customers.

Zuar Runner's NetSuite Connector uses NetSuite's SuiteTalk Web Services Platform to gain access to customer data stored in NetSuite for subsequent transformation, enhancement, analysis, and presentation.

The configurability of NetSuite is one of its biggest pros, but means the API is exceedingly complex.

At Zuar, we know a thing or two about using data strategy to optimize business efficiency. This post provides a few tips that may be of help if you are embarking on a similar exploration.

Take the NetSuite Challenge - ETL & Visualization Made Easy | Zuar
Take the NetSuite Challenge - five simple steps to visualize your NetSuite data, all completely automated
Learn How to Make Your NetSuite Data Work for You | Zuar
If your business runs on NetSuite and you’re still manually exporting data to create static reports, there’s a better way.

Watch this video to learn how the NetSuite API can enhance your reporting:

SuiteTalk Web Services

Web Services are Extensible Markup Language (XML) applications mapped to programs, objects, databases, or complex business functions.

They utilize standardized XML messaging to send and receive requests over the internet. The following protocols are used to publish, expose, and access Web Services:

  • Web Services Description Language (WSDL) - exposes an interface, interaction, and protocol mapping.
  • Simple Object Access Protocol (SOAP) - a messaging format that defines and envelope for communication via HTTP and HTTPS.

A SOAP message is an ordinary XML document that contains:

  • An Envelope element identifying the XML document as a SOAP message.
  • A header element.
  • A body element with call and response information.
  • A fault element with errors and status information.

An excellent reference document is the SuiteTalk (Web Services) Platform Guide, linked here.

Netsuite Automated Reporting | Zuar
NetSuite helps companies run sales, purchasing, inventory, and more…all in the cloud. Here’s how Zuar can help you use that data to its fullest potential.

Account Setup

The examples in this post assume that a SOAP integration record is already setup in your account. If this isn't the case, refer to the Getting Started section of the SuiteTalk Platform Guide.  

A summary of all the necessary steps can be found in our NetSuite Setup Instructions blog post.

NetSuite API Version

The API is versioned, with each version being specified by a WSDL file.  In this post, we will use the 2017_1 version.  If you use a different version, it may be necessary to modify the XML in these examples to match your API version.  These resources fully describe this version used in this post:

The SOAP Schema Browser is used in conjunction with the SuiteTalk (Web Services) Records Guide available via help within your account.

Additional information can be found by searching help for NetSuite WSDL and XSD Structure.

Sending SOAP Messages

Many tools are available for interactively creating, sending, and analyzing SOAP requests.  This post uses Postman - a cross-platform application that can be used to issue SOAP requests using OAuth1.0 for authentication.

Postman's documentation discusses OAuth1.0, but it doesn't describe the technique necessary to authenticate with the NetSuite API.

The key to using Postman with NetSuite is to provide pre-request JavaScript code, run prior to invoking an operation.  The pre-request code creates a nonce and timestamp, stores it, and makes it available for use by the request that follows.

Initial Setup

We have to do a little setup within Postman prior to issuing our first SOAP request to the API.  First we'll create an environment in which to store values that can be used by any requests.

Then we'll create a collection that will keep the requests that we create, and their results, in a single place.

Create an Environment

Postman environments are collections of variables and values.  To create the environment that will contain your account information and credentials:

  1. Click the gear icon in the top-right corner of the Postman UI.

  2. In the Manage Environments popup that appears, click Add.

  3. Provide an Environment Name, such as NetSuite Environment.

  4. Add the following Variables and Initial Values:

    • ACCOUNT -- the account's ACCOUNT ID
    • CONSUMER_KEY -- the integration record's CONSUMER KEY
    • CONSUMER_SECRET -- the integration record's CONSUMER SECRET
    • TOKEN_ID -- the role's TOKEN ID
    • TOKEN_SECRET -- the role's TOKEN SECRET
    • WEBSERVICES_URL -- the NetSuite WebServices URL
  5. Click Add

  6. Once added, ensure that the environment is active. If it doesn't appear in the top-level of the pulldown to the left of the gear, use the pulldown to select it.

Once added, ensure that the environment is active.  If it doesn't appear in the top-level of the pulldown to the left of the gear, use the pulldown to select it.

A Postman Environment for NetSuite

Your environment should be similar to the above, except that initial and current values will not be hidden.

See the Postman documentation on variables for more information.

Create a New Collection

Collections are used to organize associated groups of requests and responses.  In Postman, create a collection, such as NetSuite API Demo.

Looking to better understand how these data tools can give insight into the right direction for your company? Check out how our data strategy service can help you today.

First SOAP Request

With setup out of the way, it's time to send a SOAP request.  We'll create a request that will return all CustomList records from the account specified in the environment.

The request will return the contents of all custom lists (i.e., all customList recordType) in the account.

  1. Within the collection, create a new request named CustomList.

  2. Within the CustomList request:

    1. Near the top-left of the request UI, select a POST operation.

    2. To the right of POST, enter {{webservices_url}} as the URL. When the request is executed, {{webservices_url}} will be replaced by the value of webservices_url from the environment.

    3. Under POST, click Pre-req. In the text area below Pre-req, add the Javascript code found at the end of this document. The code will be executed prior to to invoking the operation that we are currently defining. When executed, the code will add these variables/values to the Postman environment: signature, nonce, and timestamp.

    4. Under POST, click Headers. Add the following Key / Value pairs:

      KEY             VALUE
      Content-Type    text/xml
      SOAPAction      search
      

      NOTE: the value for SOAPAction must match the operation that is present in the body of the request. In this example, our request body performs a CustomListSearh; its corresponding SOAPAction is search.

    5. Under POST, ensure that Auth -> Type is set to No Auth.

    6. Under POST, nothing needs to be done to Params.

    7. Under POST, click Body.

      Add the following XML which defines the entire request that will be sent:

      <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
        <soap-env:Header>
          <tokenPassport>
            <account>{{ACCOUNT}}</account>
            <consumerKey>{{CONSUMER_KEY}}</consumerKey>
            <token>{{TOKEN_ID}}</token>
            <nonce>{{nonce}}</nonce>
            <timestamp>{{timestamp}}</timestamp>
            <signature algorithm="HMAC-SHA1">{{signature}}</signature>
          </tokenPassport>
          <preferences>
            <runServerSuiteScriptAndTriggerWorkflows>
              false
            </runServerSuiteScriptAndTriggerWorkflows>
          </preferences>
          <searchPreferences>
            <pageSize>1000</pageSize>
            <bodyFieldsOnly>false</bodyFieldsOnly>
          </searchPreferences>
        </soap-env:Header>
        <soap-env:Body>
          <ns0:search xmlns:ns0="urn:messages_2017_1.platform.webservices.netsuite.com">
            <ns0:searchRecord xmlns:ns2="urn:customization_2017_1.setup.webservices.netsuite.com"
                              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                              xsi:type="ns2:CustomListSearch">
              <ns2:basic/>
            </ns0:searchRecord>
          </ns0:search>
        </soap-env:Body>
      </soap-env:Envelope>
      
    8. Click Send.

The response will appear in a separate window.  In the following image, one can see both the SOAP request as well as a portion of the response.

The response can be saved, if you desire.  Additional requests can be created within the collection and additional environments can be created for other accounts.

All of this will be retained within the collection by Postman.

Second SOAP Request

Rather than get everything that's available, let's drill-down a little and get only the contents of a single custom list using the custom list's internalId.  To do this, we'll have to issue a get instead of a search.

  1. Create a new request in your collection.

  2. Set SOAPAction to get.

  3. Determine the internalId of the desired custom list. Note that the internal id will be account-specific. This example uses 257. If 257 is valid in your account, it will almost certainly return the contents of a different custom list.

  4. Use the following POST body:

    <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
      <soap-env:Header>
        <tokenPassport>
          <account>{{ACCOUNT}}</account>
          <consumerKey>{{CONSUMER_KEY}}</consumerKey>
          <token>{{TOKEN_ID}}</token>
          <nonce>{{nonce}}</nonce>
          <timestamp>{{timestamp}}</timestamp>
          <signature algorithm="HMAC-SHA1">{{signature}}</signature>
        </tokenPassport>
        <preferences>
          <runServerSuiteScriptAndTriggerWorkflows>
            false
          </runServerSuiteScriptAndTriggerWorkflows>
        </preferences>
        <searchPreferences>
          <pageSize>1000</pageSize>
          <bodyFieldsOnly>false</bodyFieldsOnly>
        </searchPreferences>
      </soap-env:Header>
      <soap-env:Body>
        <get xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:platformCore="urn:core_2017_1.platform.webservices.netsuite.com">
          <baseRef internalId="257" type="customList" xsi:type="platformCore:RecordRef">
            <platformCore:name/>
          </baseRef>
        </get>
      </soap-env:Body>
    </soap-env:Envelope>
    
  5. Click Send.

In the NetSuite account I used when writing this post, internal id 257 was FAM - Month Names, as shown below.

Final SOAP Request

As a final example, let's get a list of all records of a specified type, in this case, currency.

  1. Set SOAPAction to getAll.

  2. Use a recordType of "currency".

  3. Use the following POST body:

    <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
      <soap-env:Header>
        <tokenPassport>
          <account>{{ACCOUNT}}</account>
          <consumerKey>{{CONSUMER_KEY}}</consumerKey>
          <token>{{TOKEN_ID}}</token>
          <nonce>{{nonce}}</nonce>
          <timestamp>{{timestamp}}</timestamp>
          <signature algorithm="HMAC-SHA1">{{signature}}</signature>
        </tokenPassport>
        <preferences>
          <runServerSuiteScriptAndTriggerWorkflows>
            false
          </runServerSuiteScriptAndTriggerWorkflows>
        </preferences>
        <searchPreferences>
          <pageSize>1000</pageSize>
          <bodyFieldsOnly>false</bodyFieldsOnly>
        </searchPreferences>
      </soap-env:Header>
      <soap-env:Body>
        <getAll xmlns="urn:messages_2017_1.platform.webservices.netsuite.com">
          <record recordType="currency"/>
        </getAll>
      </soap-env:Body>
    </soap-env:Envelope>
    
  4. Click Send.

The request should return the list of currencies supported in the account, as shown below.

Javascript Pre-Request

This is the Javascript code that must be added to Pre-req for a request to authenticate properly:

/*
Taken from: https://gist.github.com/michoelchaikin/2a4916e0917aa0f79b8eb9d7fe275363

Usage:
1. Define a NetSuite environment in Postman
   (https://www.getpostman.com/docs/postman/environments_and_globals/manage_environments)
   with the following keys set: ACCOUNT, CONSUMER_KEY, CONSUMER_SECRET, TOKEN_ID, TOKEN_SECRET
   
2. Add this script to your "Pre Request Script" in Postman
   (https://www.getpostman.com/docs/postman/scripts/pre_request_scripts) 

3. Add the Token Passport in your request with variable placeholders

   <tokenPassport xmlns="urn: messages_2017_2.platform.webservices.netsuite.com"
                  xmlns:ns1="urn:core_2017_2.platform.webservices.netsuite.com">
       <ns1:account>{{ACCOUNT}}</ns1:account>
       <ns1:consumerKey>{{CONSUMER_KEY}}</ns1:consumerKey>
       <ns1:token>{{TOKEN_ID}}</ns1:token>
       <ns1:nonce>{{nonce}}</ns1:nonce>
       <ns1:timestamp>{{timestamp}}</ns1:timestamp>
       <ns1:signature algorithm="HMAC-SHA1">{{signature}}</ns1:signature>
   </tokenPassport>
*/

let account = pm.environment.get("ACCOUNT");
let consumerKey = pm.environment.get("CONSUMER_KEY");
let consumerSecret = pm.environment.get("CONSUMER_SECRET");
let tokenId = pm.environment.get("TOKEN_ID");
let tokenSecret = pm.environment.get("TOKEN_SECRET");
    
let timestamp = new Date().getTime().toString().substring(0, 10);
let nonce = CryptoJS.lib.WordArray.random(10).toString();
let baseString = `${account}&${consumerKey}&${tokenId}&${nonce}&${timestamp}`;
let key = `${consumerSecret}&${tokenSecret}`;
let signature = CryptoJS.HmacSHA1(baseString, key).toString(CryptoJS.enc.Base64);

pm.environment.set("signature", signature);
pm.environment.set("nonce", nonce);
pm.environment.set("timestamp", timestamp);

Additional Tips

XML Requests

A discussion of how to construct valid XML for SOAP requests is beyond the scope of this note.  We can, however, share a few helpful pointers.

To find examples of XML requests search for "SOAP Web Services Operations" in NetSuite help.

In an example, the content between <soapenv:Body> and </soapenv:Body> can be dropped-in to the body, replacing similar content in the examples above.

Some adjustment of namespaces may be necessary for this to work.

Proxies

Postman can be used with a proxy.  This is especially handy when one needs to compare differences between requests and results.

To use a proxy, access Preferences -> Proxy, enable Global Proxy Configuration, and add the IP address/port of your proxy.  Disable System Proxy.

Once this is done, your requests and responses will appear in Charles, or whatever proxy you are using.

Enhancing Reporting and Dashboards With the NetSuite API

Watch this

Next Steps

We hope that we've helped you better understand the NetSuite Web Services API. Remember to consider using Zuar Runner to get the most out of your NetSuite data. Learn more about our NetSuite Challenge!

Zuar Runner ELT Data Staging Platform | Zuar
Zuar Runner is a fast, lightweight, automated data staging platform. Connect to APIs, Databases, or Flat Files to model your data in preparation for analytics.
Zuar | NetSuite Integration
Start the NetSuite challenge in five easy steps to get your 2-week trial of automated visual analytics. If you like it, we’ll keep it running.