Accounting systems like NetSuite are important for managing and optimizing business operations through automated processes and integrated workflows. NetSuite is a cloud-based enterprise resource planning (ERP) system that has a full suite of tools for financial management, customer relationship management, e-commerce, and more.
The NetSuite API allows developers to build custom integrations that connect NetSuite to other systems, automate complex workflows, and synchronize data across different systems. Using this API, developers can automate tasks, such as invoicing, and streamline expense management. This is key to building efficient automated solutions that reduce manual effort and increase accuracy.
Why the NetSuite SOAP API Is Important
The NetSuite SOAP API is a gateway to the core functionality of NetSuite that allows you to interact with the platform programmatically. You can use it to perform operations such as creating and updating records, managing customer data, processing orders, and generating financial reports. The SOAP API allows you to integrate NetSuite into your existing systems and automate business-critical processes, also ensuring data is consistent across all applications. For example, you can automate a workflow like order fulfillment or sync customer data between NetSuite and other platforms, reducing manual errors and increasing operational efficiency.
Here are some examples of how you can use the API:
- Automating accounts receivable (AR): The NetSuite SOAP API can automate the overall AR process, from generating invoices to collecting payments. This reduces delays that might occur with manual invoicing and also minimizes human errors in invoice generation and payment tracking.
- Automating financial reports: The NetSuite API can integrate with reporting tools like Tableau, which provide real-time insight into the business's financial performance. This automation can generate accurate and up-to-date financial statements, like profit and loss reports and cash flow statements. Automatically updating these reports helps businesses quickly identify trends, monitor key financial metrics, and make data-driven decisions.
- Integrating NetSuite with e-commerce platforms: The NetSuite API integrates with various e-commerce platforms, such as Shopify, synchronizing order data, inventory levels, and customer information.
How to Integrate with NetSuite API
NetSuite supports token-based authentication, user credentials, and outbound single sign-on (SSO) to authenticate with its SOAP API.
This guide focuses on token-based authentication, which involves creating a signature using your NetSuite account credentials and including it with other credentials in the SOAP header of each request.
To interact with the SOAP API, you need to do the following:
- Create a user role with the proper admin permissions, which include SOAP Web Services.
- Create a new integration record for token-based authentication and obtain the consumer key and secret.
- Create a new access token and obtain the token ID and secret.
More information is available in the official docs.
Making a Request to the NetSuite SOAP API in Postman
Tools like Postman are commonly used for testing APIs. Postman allows you to configure the headers, construct the request body, and send the request to the NetSuite SOAP API. This helps you to verify that your requests are correctly formed and that you're receiving the expected responses.
Each request to the NetSuite SOAP API should include the following details in the header:
account
: This is your NetSuite account ID, which you can obtain from Setup > Integration > Soap Web Services Preferences.consumerKey
: This is the consumer key for the integration record you created in the previous step.token
: This is the unique identifier for the token you created in the previous step. It represents a unique combination of a user, a role, and an integration record.nonce
: This is a unique, randomly generated alphanumeric string with a length between six and sixty-four characters.timestamp
: This is the current timestamp in Unix format.signature
: This is the hash value of the other authentication headers. You will learn how to construct this later.
You then create a POST request addressed to https://<netsuite_account_id>.suitetalk.api.netsuite.com/services/NetSuitePort_2022_2
. Make sure you replace the <netsuite_account_id>
with the appropriate value. Make sure also the environment you just created is active.
To generate the nonce
, timestamp
, and signature
and set them as environment variables, you add the following JavaScript code to the Pre-request Script tab:
const crypto = require("crypto-js");
const generateSignature = (
account,
consumerKey,
consumerSecret,
tokenKey,
tokenSecret
) => {
const nonce =
Math.random().toString(36).substr(2, 15) +
Math.random().toString(36).substr(2, 15);
const timestamp = Math.round(new Date().getTime() / 1000);
const key = `${consumerSecret}&${tokenSecret}`;
const baseString =
account +
"&" +
consumerKey +
"&" +
tokenKey +
"&" +
nonce +
"&" +
timestamp;
const base64hash = crypto
.HmacSHA256(baseString, crypto.enc.Utf8.parse(key))
.toString(crypto.enc.Base64);
return { nonce, timestamp, base64hash };
};
const account = pm.environment.get("netsuite_account");
const consumerKey = pm.environment.get("netsuite_consumer_key");
const consumerSecret = pm.environment.get("netsuite_consumer_secret");
const tokenKey = pm.environment.get("netsuite_token_key");
const tokenSecret = pm.environment.get("netsuite_token_secret");
const { nonce, timestamp, base64hash } = generateSignature(
account,
consumerKey,
consumerSecret,
tokenKey,
tokenSecret
);
pm.environment.set("netsuite_nonce", nonce);
pm.environment.set("netsuite_timestamp", timestamp);
pm.environment.set("netsuite_signature", base64hash);
The code imports the crypto-js
library for hashing. It then defines the generateSignature
function. This function takes a few variables: account
, consumerKey
, consumerSecret
, tokenKey
, and tokenSecret
. It creates a unique nonce
by concatenating two random Base36 strings and calculates the current Unix timestamp. It constructs a key from the consumer secret and token secret, and it generates a base string from various parameters. Using HMAC SHA-256 hashing with the key, it produces a Base64-encoded signature. It then returns the nonce
, timestamp
, and base64
hash. These values are later set as environment variables so that they can be accessed by each request.
More details on generating the signature can be found in the official docs.
To include the defined headers with the request, you then add the following code to the Body tab, making sure to select the XML data type:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="urn:platform_2022_2.webservices.netsuite.com" xmlns:platformMsgs="urn:messages_2022_2.platform.webservices.netsuite.com" xmlns:platformFaults="urn:faults_2022_2.platform.webservices.netsuite.com" xmlns:platformCommon="urn:common_2022_2.platform.webservices.netsuite.com" xmlns:platformCore="urn:core_2022_2.platform.webservices.netsuite.com" xmlns:setupCustom="urn:customization_2022_2.setup.webservices.netsuite.com">
<soap:Header>
<platformMsgs:tokenPassport>
<platformCore:account>{{netsuite_account}}</platformCore:account>
<platformCore:consumerKey>{{netsuite_consumer_key}}</platformCore:consumerKey>
<platformCore:token>{{netsuite_token_key}}</platformCore:token>
<platformCore:version>1.0</platformCore:version>
<platformCore:nonce>{{netsuite_nonce}}</platformCore:nonce>
<platformCore:timestamp>{{netsuite_timestamp}}</platformCore:timestamp>
<platformCore:signature algorithm="HMAC_SHA256">{{netsuite_signature}}</platformCore:signature>
</platformMsgs:tokenPassport>
<searchPreferences xsi:type="SearchPreferences">
<bodyFieldsOnly>false</bodyFieldsOnly>
<returnSearchColumns>true</returnSearchColumns>
<pageSize>20</pageSize>
</searchPreferences>
</soap:Header>
<soap:Body>
<search xmlns="urn:messages_2022_2.platform.webservices.netsuite.com">
<searchRecord xsi:type="ns1:TransactionSearchBasic" xmlns:ns1="urn:common_2022_2.platform.webservices.netsuite.com">
<ns1:type operator="anyOf" xsi:type="ns2:SearchEnumMultiSelectField" xmlns:ns2="urn:core_2022_2.platform.webservices.netsuite.com">
<ns2:searchValue xsi:type="xsd:string">invoice</ns2:searchValue>
</ns1:type>
</searchRecord>
</search>
</soap:Body>
</soap:Envelope>
This SOAP request envelope defines the necessary namespaces for the SOAP protocol and NetSuite web services for 2022. In the <soap:Header>
section, authentication details are included, using environment variables for account
, consumerKey
, token
, nonce
, timestamp
, and signature
. Additionally, the header specifies search preferences, such as including all fields, returning search columns, and setting the page size to 20
. The <soap:Body>
section initiates a search for transactions of type invoice
by specifying the search criteria and record type within NetSuite web services.
Making a Request to the NetSuite SOAP API in Python
While Postman is great for testing APIs and exploring their capabilities, making the API requests using code provides a more comprehensive understanding of the integration. Let's look at how you can make the same request in Python. To do this, you need to have Python installed on your local machine. Here is the Python code for the same request:
import random
import time
import hmac
import base64
import hashlib
import requests
netsuite_account = "<YOUR-NETSUITE-ACCOUNT-ID>"
netsuite_consumer_key = "<YOUR-NETSUITE-CONSUMER-KEY>"
netsuite_consumer_secret = "<YOUR-NETSUITE-CONSUMER-SECRET>"
netsuite_token_key = "<YOUR-NETSUITE-TOKEN-ID"
netsuite_token_secret = "<YOUR-NETSUITE-TOKEN-SECRET>"
def generate_signature(account, consumer_key, consumer_secret, token_key, token_secret):
# Generate a nonce (a random string)
netsuite_nonce = ''.join([random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(30)])
# Generate a timestamp
netsuite_timestamp = int(time.time())
# Construct the key and base string
key = f"{consumer_secret}&{token_secret}"
base_string = f"{account}&{consumer_key}&{token_key}&{netsuite_nonce}&{netsuite_timestamp}"
# Compute the HMAC SHA-256 hash
hashed = hmac.new(key.encode('utf-8'), base_string.encode('utf-8'), hashlib.sha256)
netsuite_signature = base64.b64encode(hashed.digest()).decode('utf-8')
return (netsuite_nonce, netsuite_timestamp, netsuite_signature)
netsuite_nonce, netsuite_timestamp, netsuite_signature = generate_signature(netsuite_account, netsuite_consumer_key, netsuite_consumer_secret, netsuite_token_key, netsuite_token_secret)
request_body = f"""
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="urn:platform_2022_2.webservices.netsuite.com" xmlns:platformMsgs="urn:messages_2022_2.platform.webservices.netsuite.com" xmlns:platformFaults="urn:faults_2022_2.platform.webservices.netsuite.com" xmlns:platformCommon="urn:common_2022_2.platform.webservices.netsuite.com" xmlns:platformCore="urn:core_2022_2.platform.webservices.netsuite.com" xmlns:setupCustom="urn:customization_2022_2.setup.webservices.netsuite.com">
<soap:Header>
<platformMsgs:tokenPassport>
<platformCore:account>{netsuite_account}</platformCore:account>
<platformCore:consumerKey>{netsuite_consumer_key}</platformCore:consumerKey>
<platformCore:token>{netsuite_token_key}</platformCore:token>
<platformCore:version>1.0</platformCore:version>
<platformCore:nonce>{netsuite_nonce}</platformCore:nonce>
<platformCore:timestamp>{netsuite_timestamp}</platformCore:timestamp>
<platformCore:signature algorithm="HMAC_SHA256">{netsuite_signature}</platformCore:signature>
</platformMsgs:tokenPassport>
<searchPreferences xsi:type="SearchPreferences">
<bodyFieldsOnly>false</bodyFieldsOnly>
<returnSearchColumns>true</returnSearchColumns>
<pageSize>20</pageSize>
</searchPreferences>
</soap:Header>
<soap:Body>
<search xmlns="urn:messages_2022_2.platform.webservices.netsuite.com">
<searchRecord xsi:type="ns1:TransactionSearchBasic" xmlns:ns1="urn:common_2022_2.platform.webservices.netsuite.com">
<ns1:type operator="anyOf" xsi:type="ns2:SearchEnumMultiSelectField" xmlns:ns2="urn:core_2022_2.platform.webservices.netsuite.com">
<ns2:searchValue xsi:type="xsd:string">invoice</ns2:searchValue>
</ns1:type>
</searchRecord>
</search>
</soap:Body>
</soap:Envelope>
"""
response = requests.post(
url=f"""https://{netsuite_account}.suitetalk.api.netsuite.com/services/NetSuitePort_2022_2""",
data=request_body
)
This code begins by defining credentials and a function to generate a nonce
, timestamp
, and signature
using HMAC SHA-256 hashing. These values are used to construct a SOAP envelope that includes authentication details in the header and a search query for invoice
transactions in the body. The code then sends the SOAP request via a POST request to the NetSuite API using the requests
library.
The Apideck Unified Accounting API
Integrating with multiple accounting systems can be complex and time-consuming. Constructing and managing SOAP requests also requires careful attention to detail to avoid errors. Each system you need to connect to has its own API with unique requirements and intricacies, which adds to the complexity of integration.
The Apideck Unified Accounting API provides a single integration point for various accounting platforms, including NetSuite. This helps to abstract the complexities of individual APIs, simplifying the integration process.
Some benefits of using the Apideck Unified Accounting API include the following:
- Reduced maintenance: With a unified API, the maintenance burden is significantly reduced. Updates or changes in the underlying accounting systems do not require modifications to multiple integration points. Instead, you need to maintain only a single integration with the Apideck API.
- Time and resource savings: The unified API handles the complexities of different accounting systems, allowing you to focus on building features rather than managing multiple integrations.
Before making an API request to the NetSuite API with the Apideck Proxy API, you need to make sure that you have configured the NetSuite connection on Apideck.
The following TypeScript code uses Proxy API to retrieve invoices from NetSuite:
import axios from "axios";
const headers = {
"x-apideck-app-id": "<APP-ID>",
Authorization: "Bearer <API-KEY>",
"x-apideck-consumer-id": "<CONSUMER-ID>", // For the NetSuite consumer
"x-apideck-service-id": "", // Optionally include service ID
"x-apideck-downstream-url": "https://unify.apideck.com/accounting/invoices",
};
async function retrieveNetSuiteInvoices() {
try {
const response = await axios.get("https://unify.apideck.com/proxy", {
headers,
});
console.log("Response data:", response.data);
} catch (error) {
console.error("Error making API request:", error);
}
}
retrieveNetSuiteInvoices();
This code sets up the necessary headers for authentication, including your Apideck app ID and API key. The retrieveNetSuiteInvoices
function sends a GET request to the Proxy API endpoint, including all the defined headers. It then logs the response data or any errors that occur during the request.
Conclusion
In this guide, you learned how to integrate with the NetSuite SOAP API. As you have seen, direct integration with the NetSuite API can be complex as it involves accurately constructing all the required headers, managing SOAP request structures, and handling XML data. These tasks are prone to errors, are time-consuming, and require significant effort to ensure accuracy.
To streamline your accounting integrations and avoid the complexities of direct API integration, try using the Apideck Accounting API and NetSuite Connector. By providing a single integration point, Apideck simplifies the process, reduces maintenance overhead, and saves valuable development time and resources. Sign up to Apideck today to start simplifying your accounting integrations.
Ready to get started?
Scale your integration strategy and deliver the integrations your customers need in record time.