How to Pass JSON Parameters to a NetSuite RESTlet

Send companyname, subsidiary and isperson in a RESTlet POST body, fix the content type, and verify the company customer created from those values.

The quick answer

Use POST with Content-Type: application/json and put the business parameters in the JSON body. A RESTlet's post(data) receives that object directly, so the handler can read data.companyname, data.subsidiary and data.isperson. Keep the script and deployment identifiers in the deployment URL.

This walkthrough uses a local API exercise and an example customer record. It does not deploy a RESTlet or contact a production NetSuite endpoint. Person and operating-company names are fictional.

Before you start

Your real RESTlet must already be deployed, expose a POST entry point and allow the authenticated role. Use the deployment's External URL, valid authentication, and the Customer record permission at Create or higher, with access to the intended subsidiary. The curl examples use OAuth 2.0 bearer-token authentication: TOKEN must be an OAuth 2.0 access token for the intended account/environment and authorized RESTlet role. Token-Based Authentication uses a signed OAuth 1.0 header instead; it does not use this Bearer syntax. Keep real credentials outside source files and screenshots.

The code shown uses SuiteScript 2.1 and N/record. It creates a Company customer, so the body supplies isperson:false and companyname. In OneWorld, the subsidiary internal ID must exist and be accessible to the role. In this local example, 6 means Pacific Operations. These IDs are examples and must not be copied into another account. Including subsidiary assumes OneWorld. Check the selected form and any required customer status, currency, tax, sales-rep or custom fields. Deployment audience, user events, workflows, duplicate detection and numbering rules can alter or reject the save. This guide starts after script upload/deployment and authentication setup; the local replay verifies none of those real-account prerequisites.

01. Inspect the JSON body and handler

The prepared customer.json contains three values:

{
  "companyname": "Cedar Ridge Industrial",
  "subsidiary": 6,
  "isperson": false
}

Match the property names to the handler. The handler shown beside the local terminal reads values from data, sets the company fields on a new customer, calls customer.save(), and wraps the returned internal ID with return . It has a custom guard that rejects a non-object argument. The relevant handler is:

/**
 * @NApiVersion 2.1
 * @NScriptType Restlet
 */
define(['N/record'], (record) => {
  const post = (data) => {
    if (!data || typeof data !== 'object') {
      throw new Error('Request body must be a JSON object.');
    }
    const customer = record.create({type: record.Type.CUSTOMER});
    customer.setValue({fieldId: 'isperson', value: data.isperson});
    customer.setValue({fieldId: 'companyname', value: data.companyname});
    customer.setValue({fieldId: 'subsidiary', value: data.subsidiary});
    const id = customer.save();
    return {id};
  };
  return {post};
});

This is a minimal teaching handler. Before exposing it in a real integration, validate the non-null object, reject arrays, require companyname, verify subsidiary access, and require the boolean isperson value.

false is a JSON boolean, not the string "false". SuiteScript 2.x checkbox fields accept boolean values. Individual customers need a different name-field setup, such as first and last name; do not switch isperson without changing validation and field mapping.

Local example: the prepared JSON supplies the company name, subsidiary 6 and boolean false.

02. Compare a plain-text request

The first local call deliberately declares the prepared body as plain text:

curl -i -X POST "$RESTLET_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @customer.json

Although the bytes look like JSON, text/plain passes a string to the POST entry point. A handler could parse that string explicitly with JSON.parse(); this handler does not. The local trace shows post(data): string. The example's custom guard rejects it with Request body must be a JSON object. No customer is created by that request.

The local wrapper reports HTTP 400. That validation text and status are part of this example, not a guarantee of NetSuite's native error response. A missing or unsupported required Content-Type can also cause HTTP 415 before the handler runs.

Local example: text/plain makes post(data) a string. Enlarge to read the custom validation message.

03. Send JSON and read the returned ID

Change the content type while keeping the same JSON file:

curl -i -X POST "$RESTLET_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary @customer.json

The local trace now shows post(data): object and isperson: boolean false. The handler creates the customer and returns:

{"id":73001}

record.save() returns an internal ID; the handler chooses to return an object containing it. Your RESTlet defines its response shape. The 204/Location behavior belongs to the REST Record Service create operation. A RESTlet response body comes from the handler, while its execution and platform error handling affect the HTTP status.

Local example: application/json supplies an object and boolean false. The handler returns internal ID 73001.

04. Verify the company customer

Open the customer represented by the local replay using its record link. For a real request, use the returned internal ID to locate the record in NetSuite.

In Primary Information, verify Cedar Ridge Industrial, Type Company and Company Name Cedar Ridge Industrial. The example's display Customer ID is CUST73001. A Customer ID and a record's internal ID are separate identifiers; they need not use the same digits.

Local example: the new customer is Cedar Ridge Industrial, Type Company. CUST73001 is its display Customer ID.

Scroll to Classification and check Primary Subsidiary Pacific Operations. It matches subsidiary 6 in the request. This confirms that the company name, type and subsidiary carried through the local creation flow.

If a real request times out, check whether it created a customer before resending it. A second POST can create a duplicate.

Local example: Primary Subsidiary is Pacific Operations, matching subsidiary 6 in the JSON body.

Common questions

Where do I put RESTlet POST parameters in Postman?

Choose POST, put the business fields in Body as raw JSON, and send Content-Type application/json. Use the deployed RESTlet's external URL with its script and deployment identifiers, plus the required authentication.

Why does my RESTlet receive a string instead of an object?

text/plain causes the RESTlet entry point to receive a string; application/json with valid JSON supplies an object. A handler that supports plain text must parse and validate the string explicitly. This example rejects it because its custom guard requires an object.

Do I read the fields through request.parameters?

A SuiteScript RESTlet receives POST data directly as its entry-point argument. In an example named post(data), read data.companyname. Suitelet-style context.request.parameters is a different request model.

Where do GET parameters go?

The method must match an implemented entry point. GET business parameters belong in the URL query string and are passed to the RESTlet's GET entry point. POST business parameters belong in the request body. script and deploy identify the deployment.

Should isperson be false or the string F?

Send the JSON boolean false, rather than the strings false or F, for this SuiteScript 2.x handler. Pass the boolean to setValue. Other APIs or legacy code can use different conventions; keep those conventions separate from this example.

Does a RESTlet return an invoice-style Location header?

A RESTlet's script defines its response. This handler returns an object containing the saved customer's internal ID. The REST Record Service's synchronous create response is a separate API contract.

How do I avoid duplicate customers on a retry?

This handler always creates a customer, so a retry can create another one. Check whether the first request saved a record before retrying. A real integration needs a deliberate idempotency or lookup strategy that also handles concurrent requests and account duplicate-detection rules. The local example does not implement that protection.

Supporting sources