How to Code Salesforce-NetSuite Integration Using RESTlets

salesforce and netsuite integration code using restlets

Why Salesforce and NetSuite Integration Code Using RESTlets is Essential

Salesforce and netsuite integration code using restlets provides direct, real-time data synchronization between your CRM and ERP. This custom approach offers full control over business logic, avoids ongoing subscription fees, and manages complex data requirements that pre-built solutions often miss.

When Salesforce and NetSuite operate in silos, you risk data errors, manual work, and fragmented customer views. A sales update in Salesforce might not reach the finance team in NetSuite, leading to billing delays and poor service. RESTlets solve this by creating custom API endpoints in NetSuite that Salesforce can call directly, allowing you to implement the exact business logic your organization needs.

Quick Implementation Overview:

  1. NetSuite Side: Create a SuiteScript 2.x RESTlet to receive and process data.
  2. Salesforce Side: Write Apex code with HTTP callouts to send data to the RESTlet.
  3. Authentication: Use Token-Based Authentication (TBA) for secure communication.
  4. Triggers: Implement Apex triggers to automate data sync on record changes.

I’m Louis Balla, and with 15 years in digital change and my work at Nuage overseeing NetSuite integrations, I’ve seen how custom RESTlet solutions open up operational efficiency that standard connectors cannot match.

Simple guide to salesforce and netsuite integration code using restlets:

Understanding the Foundation: What Are NetSuite RESTlets?

NetSuite RESTlets are custom, server-side scripts built with SuiteScript 2.x (NetSuite’s JavaScript-based language) that create custom API endpoints within your NetSuite environment. They act as controlled entry points, defining what data can be received, how it’s processed, and what happens next.

A key feature is that RESTlets are stateless. Each request from Salesforce is handled independently, making the integration highly scalable. This flexibility allows you to define precise actions, like creating customers or sales orders, and implement custom integration logic for complex business requirements that standard connectors can’t handle. For technical specifications, refer to NetSuite’s official RESTlet documentation.

Why RESTlets are Ideal for Salesforce Integration

RESTlets are particularly well-suited for Salesforce integration for several reasons:

  • Full control over business logic: Implement precise validation rules, complex data changes, and workflows custom to your business processes.
  • Handles complex data changes: Easily synchronize multi-line orders, intricate customer hierarchies, or custom fields with specific formatting.
  • No ongoing subscription fees: After the initial development cost, there are no recurring licensing fees, offering significant long-term savings.
  • Direct point-to-point communication: Salesforce communicates directly with NetSuite, reducing latency and potential points of failure.
  • Scalability: Built on NetSuite’s SuiteCloud platform, RESTlets can handle increasing transaction volumes as your business grows.

How RESTlets Compare to Other NetSuite Integration Methods

Let’s compare RESTlets with SuiteTalk (NetSuite’s SOAP-based web services), another common custom integration method.

Feature RESTlets SuiteTalk (SOAP Web Services)
Development Effort Requires SuiteScript knowledge. Higher initial effort for custom logic. Leverages WSDL and standard APIs. Can be quicker for standard CRUD operations.
Cost Model One-time development cost. No recurring fees. One-time development cost. No recurring fees.
Flexibility High. Full control over logic and data format (JSON is common). Moderate. Limited to predefined operations and XML structure.
Maintenance Requires developer knowledge for updates. Can be complex due to WSDL updates and XML parsing.
Ideal Use Case Complex custom business logic, real-time sync, integrating with modern RESTful systems. Standard CRUD operations, integrating with legacy SOAP-based systems.

While SuiteTalk is effective for standard operations, RESTlets provide the power and control needed for sophisticated salesforce and netsuite integration code using restlets projects with complex business rules.

Prerequisites: Preparing Your Salesforce and NetSuite Environments

Before writing any salesforce and netsuite integration code using restlets, proper environment setup is crucial to prevent future issues. You will need administrator access in both NetSuite and Salesforce. All development and testing should be performed in sandbox environments to avoid disrupting live operations.

NetSuite Environment Setup

Prepare your NetSuite environment by enabling features and configuring secure authentication.

  1. Enable SuiteCloud Features: Steer to Setup > Company > Enable Features. On the SuiteCloud tab, enable SuiteScript (ensure 2.x is active), REST Web Services, and Token-Based Authentication.
  2. Create an Integration Record: Go to Setup > Integration > Manage Integrations > New. Name your integration (e.g., “Salesforce Customer Sync”), check the Token-Based Authentication box, and save.
  3. Secure Credentials: NetSuite will display the Consumer Key and Consumer Secret only once. Copy and store them securely immediately.
  4. Generate Access Tokens: In the Settings portlet on your home page, click Manage Access Tokens. Create a new token, selecting your integration application and an appropriate user. The Token ID and Token Secret will also be shown only once—copy and store them with your other credentials.

These four credentials are required for secure authentication from Salesforce.

NetSuite "Enable Features" page with SuiteCloud options highlighted - salesforce and netsuite integration code using restlets

Salesforce Environment Setup

Salesforce setup requires a Developer, Enterprise, or Unlimited Edition to support Apex development. Always use a Salesforce Sandbox for development to protect your live data.

The critical step is configuring Remote Site Settings to allow outbound calls to NetSuite.

  1. Steer to Setup > Security > Remote Site Settings and click New Remote Site.
  2. Give it a descriptive name like “NetSuite RESTlet Endpoint”.
  3. Enter your NetSuite RESTlet URL in the Remote Site URL field. You will get the exact URL after deploying the RESTlet, but you can use your NetSuite account’s base URL as a placeholder for now.

With these configurations, both systems are ready for integration.

Salesforce Remote Site Settings configuration screen - salesforce and netsuite integration code using restlets

Part 1: Creating and Deploying Your NetSuite RESTlet Script

This section covers the NetSuite portion of the salesforce and netsuite integration code using restlets. We will write and deploy a SuiteScript 2.x script to create a custom API endpoint that receives data from Salesforce and creates a Customer record in NetSuite.

Step 1: Write the RESTlet Code to Receive Data

Create a JavaScript file (sf_customer_integration_restlet.js) with the following SuiteScript 2.x code. This RESTlet will process an incoming JSON payload from Salesforce to create a new Customer in NetSuite.

/**
 * @NApiVersion 2.x
 * @NScriptType Restlet
 * @NModuleScope SameAccount
 */
define(['N/record', 'N/log'], function(record, log) {

    function post(requestBody) {
        log.debug('RESTlet Request Received', JSON.stringify(requestBody));
        var response = {};

        try {
            var sfAccount = requestBody;

            if (!sfAccount || !sfAccount.name) {
                throw new Error('Missing required data: Account Name is required.');
            }

            var customerRecord = record.create({
                type: record.Type.CUSTOMER,
                isDynamic: true
            });

            customerRecord.setValue({ fieldId: 'companyname', value: sfAccount.name });
            customerRecord.setValue({ fieldId: 'entityid', value: sfAccount.name });
            customerRecord.setValue({ fieldId: 'phone', value: sfAccount.phone || '' });
            customerRecord.setValue({ fieldId: 'email', value: sfAccount.email || '' });

            if (sfAccount.billingStreet && sfAccount.billingCity && sfAccount.billingState && sfAccount.billingZip) {
                customerRecord.selectNewLine({ sublistId: 'addressbook' });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'defaultbilling', value: true });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'defaultshipping', value: true });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'addr1', value: sfAccount.billingStreet });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'city', value: sfAccount.billingCity });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'state', value: sfAccount.billingState });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'zip', value: sfAccount.billingZip });
                customerRecord.setCurrentSublistValue({ sublistId: 'addressbook', fieldId: 'country', value: sfAccount.billingCountry || 'US' });
                customerRecord.commitLine({ sublistId: 'addressbook' });
            }

            var newCustomerId = customerRecord.save();
            log.audit('Customer Created Successfully', 'New Customer ID: ' + newCustomerId);

            response.status = 'SUCCESS';
            response.customerId = newCustomerId;
            response.message = 'NetSuite Customer created successfully.';

        } catch (e) {
            log.error('RESTlet Error', 'Error creating customer: ' + e.message);
            response.status = 'ERROR';
            response.message = 'Failed to create NetSuite Customer: ' + e.message;
        }

        return response;
    }

    return {
        post: post
    };

});

Complete SuiteScript 2.x code for the Customer creation RESTlet - salesforce and netsuite integration code using restlets

This script’s post function executes when Salesforce sends data. It uses the N/record module to create a customer record programmatically and includes error handling and logging for easier troubleshooting.

Step 2: Deploy the RESTlet in NetSuite

Deploying the script makes it accessible to Salesforce.

  1. Upload File: In NetSuite, steer to Documents > Files > SuiteScripts and upload your .js file.
  2. Create Script Record: Go to Customization > Scripting > Scripts > New, select your uploaded file, and save the script record. Give it a descriptive name and a unique ID.
  3. Deploy Script: From the script record, click Deploy Script. Set the Status to Released and the Log Level to Debug. On the Audience tab, define which roles can access the endpoint. For production, restrict this to a specific integration role.

After saving the deployment, NetSuite generates a unique External URL. This is the endpoint Salesforce will call. Copy this URL, as you will need it for the Apex code in the next part.

NetSuite Script Deployment page showing the generated External URL - salesforce and netsuite integration code using restlets

Part 2: The Complete Salesforce and NetSuite Integration Code Using RESTlets

With the NetSuite RESTlet deployed, we can now build the Salesforce side of the salesforce and netsuite integration code using restlets. This involves an Apex class to make HTTP callouts and a trigger to initiate the process.

Step 1: Write the Apex Class to Call the RESTlet

Create an Apex class named NetSuiteIntegrationService to send Account data to the NetSuite RESTlet. We use the @future(callout=true) annotation to run the callout asynchronously, preventing governor limit issues and ensuring a smooth user experience.

Important on Security

The example below uses placeholder values for credentials and endpoints. In a real implementation, you must replace these with your actual values. For maximum security, store credentials in Named Credentials or protected Custom Metadata Types in Salesforce, not directly in the code.

public class NetSuiteIntegrationService {
    // PLACEHOLDER VALUES – replace with your actual credentials and endpoint URL.
    // Store credentials securely, not in code.
    private static final String NETSUITE_RESTLET_URL = 'YOUR_NETSUITE_RESTLET_EXTERNAL_URL';
    private static final String CONSUMER_KEY        = 'YOUR_CONSUMER_KEY';
    private static final String CONSUMER_SECRET     = 'YOUR_CONSUMER_SECRET';
    private static final String TOKEN_ID            = 'YOUR_TOKEN_ID';
    private static final String TOKEN_SECRET        = 'YOUR_TOKEN_SECRET';
    private static final String ACCOUNT_ID          = 'YOUR_NETSUITE_ACCOUNT_ID';

    @future(callout=true)
    public static void sendAccountToNetSuite(String accountJson) {
        HttpRequest req = new HttpRequest();
        req.setMethod('POST');
        req.setEndpoint(NETSUITE_RESTLET_URL);
        req.setHeader('Content-Type', 'application/json');

        // Build OAuth 1.0 Authorization header for Token-Based Authentication
        String timestamp = String.valueOf(Datetime.now().getTime() / 1000);
        String nonce     = generateNonce();
        String signature = generateOAuthSignature(timestamp, nonce);

        String authHeader = 'OAuth field="' + ACCOUNT_ID + '",' + // Note: NetSuite uses 'field' for the Account ID
            'oauth_consumer_key="'  + CONSUMER_KEY  + '",' +
            'oauth_token="'        + TOKEN_ID      + '",' +
            'oauth_signature_method="HMAC-SHA256",' +
            'oauth_timestamp="'    + timestamp     + '",' +
            'oauth_nonce="'        + nonce         + '",' +
            'oauth_version="1.0",' +
            'oauth_signature="'    + signature     + '"';

        req.setHeader('Authorization', authHeader);
        req.setBody(accountJson);

        try {
            Http http       = new Http();
            HttpResponse res = http.send(req);

            if (res.getStatusCode() == 200) {
                System.debug('NetSuite Integration Success: ' + res.getBody());
                // Optional: Parse response and update Salesforce record with NetSuite ID
            } else {
                System.debug('HTTP Error: Status Code ' + res.getStatusCode() + ', Body: ' + res.getBody());
                // Implement error logging to a custom object
            }
        } catch (Exception e) {
            System.debug('Callout Exception: ' + e.getMessage());
            // Implement robust error handling
        }
    }

    private static String generateNonce() {
        // Generates a random 20-character string for the nonce
        return EncodingUtil.base64Encode(Crypto.generateAesKey(128)).substring(0, 20);
    }

    private static String generateOAuthSignature(String timestamp, String nonce) {
        // Simplified OAuth 1.0 signature generation.
        // A full implementation should handle all parameter encoding in production.
        String baseString = 'POST&' + EncodingUtil.urlEncode(NETSUITE_RESTLET_URL, 'UTF-8') + '&' +
            EncodingUtil.urlEncode('oauth_consumer_key=' + CONSUMER_KEY +
            '&oauth_nonce=' + nonce +
            '&oauth_signature_method=HMAC-SHA256' +
            '&oauth_timestamp=' + timestamp +
            '&oauth_token=' + TOKEN_ID +
            '&oauth_version=1.0', 'UTF-8');

        String signingKey = EncodingUtil.urlEncode(CONSUMER_SECRET, 'UTF-8') + '&' +
                            EncodingUtil.urlEncode(TOKEN_SECRET, 'UTF-8');

        Blob signatureBlob = Crypto.generateMac('hmacSHA256', Blob.valueOf(baseString), Blob.valueOf(signingKey));
        return EncodingUtil.urlEncode(EncodingUtil.base64Encode(signatureBlob), 'UTF-8');
    }
}

Complete Apex callout class code - salesforce and netsuite integration code using restlets

This class constructs the OAuth 1.0 header for authentication and sends the Account data as a JSON payload.

Step 2: Create the Apex Trigger to Initiate the Sync

An Apex Trigger on the Account object will automatically invoke our integration class when an Account is created or updated.

Create a new Apex Trigger named AccountTrigger:

trigger AccountTrigger on Account (after insert, after update) {
    List<Account> accountsToSync = new List<Account>();

    for (Account acc : Trigger.new) {
        Boolean needsSync = false;

        if (Trigger.isInsert) {
            needsSync = true;
        } else if (Trigger.isUpdate) {
            Account oldAcc = Trigger.oldMap.get(acc.Id);
            if (acc.Name != oldAcc.Name || acc.Phone != oldAcc.Phone || acc.BillingStreet != oldAcc.BillingStreet) {
                needsSync = true;
            }
        }

        if (needsSync) {
            accountsToSync.add(acc);
        }
    }

    if (!accountsToSync.isEmpty()) {
        for (Account acc : accountsToSync) {
            Map<String, Object> accountData = new Map<String, Object>{
                'name'          => acc.Name,
                'phone'         => acc.Phone,
                'billingStreet' => acc.BillingStreet,
                'billingCity'   => acc.BillingCity,
                'billingState'  => acc.BillingState,
                'billingZip'    => acc.BillingPostalCode,
                'billingCountry'=> acc.BillingCountry,
                'salesforceId'  => acc.Id
            };
            // One callout per record. For bulk operations consider Queueable Apex.
            NetSuiteIntegrationService.sendAccountToNetSuite(JSON.serialize(accountData));
        }
    }
}

Step 3: Adapt the Code for Common Use Cases

  • Opportunity to Sales Order – Create a trigger on the Opportunity object that fires when an Opportunity is Closed Won. Point the callout to a RESTlet that creates a Sales Order in NetSuite.
  • Case to Support Case – Sync customer support cases to NetSuite for unified service visibility.
  • Create (POST) vs. Update (PUT) – Add a put function to your RESTlet and switch the HTTP method in Apex when you need to update existing NetSuite records rather than create new ones.

Best Practices for a Robust and Scalable Integration

A successful salesforce and netsuite integration code using restlets is not just functional—it’s robust, scalable, and maintainable. Adhering to best practices ensures your integration remains reliable as your business evolves.

Designing for Real-Time vs. Batch Processing

Choosing the right processing model is a strategic decision based on your business needs.

  • Real-Time (Apex Triggers): Ideal for time-sensitive data like new customers or sales orders. This provides immediate data consistency but is subject to Salesforce governor limits.
  • Batch (Scheduled Apex): Best for high-volume, less urgent updates, such as nightly data syncs. It processes records in chunks, which is more efficient and less likely to hit limits.
  • Hybrid (Queueable Apex): Offers a balance, allowing you to chain jobs and process larger data sets asynchronously. This is useful for complex, multi-record transactions.

Most robust integrations use a hybrid approach, applying real-time syncs for critical data and batch processing for everything else.

Advanced Error Handling and Logging

Integrations can fail. A solid error-handling strategy is non-negotiable.

  • Use try-catch blocks: Implement comprehensive try-catch blocks in both your Apex and SuiteScript code.
  • Create Custom Logs: Log errors to a custom object in Salesforce or a custom record in NetSuite. Capture the payload, the error message, and the context of the operation to simplify debugging.
  • Automated Alerts: Configure email alerts for critical failures (e.g., a sales order failing to sync) to notify your IT team immediately.
  • Implement Retry Logic: For transient errors like network timeouts, build a retry mechanism with exponential backoff to prevent data loss without manual intervention.

Overcoming Common Challenges and Limitations

  • Salesforce Governor Limits: Design with limits in mind from the start. Use asynchronous patterns (@future, Queueable Apex) and bulkify your code to process records efficiently.
  • NetSuite API Concurrency Limits: If you run multiple integrations, you may hit NetSuite’s concurrent request limits. Implement a queuing mechanism and intelligent retry logic to manage the load.
  • Data Mapping: Create a detailed mapping document that specifies how fields in Salesforce correspond to fields in NetSuite. Use stable internal IDs for record lookups instead of names, which can change.
  • Secure Credential Storage: Never hardcode API keys or passwords. In Salesforce, use Named Credentials or Protected Custom Settings to store sensitive information securely.
  • Version Control: Use a version control system like Git for all your code. Employ a structured deployment process using tools like Salesforce DX and the SuiteCloud Development Framework (SDF) for manageable and trackable changes.

Frequently Asked Questions about Salesforce and NetSuite RESTlet Integration

Can I use RESTlets for bidirectional Salesforce and NetSuite integration?

Yes, bidirectional synchronization is a common use case. The pattern described in this guide (Salesforce-to-NetSuite) can be mirrored for the reverse direction. To send data from NetSuite to Salesforce, you would use a NetSuite User Event Script to trigger an outbound callout to the Salesforce REST API. On the Salesforce side, an Apex REST service would receive and process the data. This creates a complete, two-way data flow.

For more on this, see our guide on integrating with the Salesforce API.

Is it possible to integrate custom fields and objects?

Absolutely. This is a key strength of using salesforce and netsuite integration code using restlets. You have the flexibility to work with any standard or custom object. Simply include the custom field API names in the JSON payload from Salesforce and map them to the corresponding custom field script IDs (e.g., custentity_my_custom_field) in your SuiteScript code.

How does this custom code approach compare to using a pre-built connector?

The custom code approach offers maximum flexibility and control over business logic, with no recurring subscription fees. It is ideal for businesses with unique processes that off-the-shelf solutions cannot accommodate. However, it requires technical expertise for development and maintenance.

Pre-built connectors offer faster initial setup for standard scenarios but are less flexible, incur ongoing subscription costs, and may not adapt to evolving business needs. At Nuage, we help you determine the right solution for your specific requirements, whether it’s a custom build or another approach.

Conclusion: Opening up Efficiency with a Connected Tech Stack

Integrating Salesforce and NetSuite with custom salesforce and netsuite integration code using restlets eliminates data silos and automates critical business processes. When your sales, finance, and operations teams work from a single source of truth, your entire organization becomes more efficient and makes smarter, data-driven decisions.

Explanation of NetSuite RESTlets or SuiteScript integration - salesforce and netsuite integration code using restlets

While this approach requires an initial technical investment, the return is significant: complete control over your integration logic, no recurring subscription fees, and a scalable solution that adapts to your business. You can build the exact workflows, validation rules, and error handling your business needs—something generic connectors rarely offer.

At Nuage, we have over 20 years of experience helping businesses design and implement robust integration strategies. Our expertise is in understanding your unique operational goals and building the right solution. We don’t just sell software; we deliver custom-fit optimizations for platforms like NetSuite.

Ready to connect your critical business systems? Our Salesforce Stratus services team specializes in creating seamless integrations that drive efficiency. Let’s build a solution that works for you.

What to read next

The Analytics Advantage: Seamlessly Integrating NetSuite and Salesforce Data

business analytics integrate netsuite and salesforce data

From Planning to Profit: How NetSuite Powers Professional Services

NetSuite professional services

Beyond the Basics: Mastering Advanced Reporting in NetSuite

netsuite advanced reporting