NetSuite REST API Sales Order and Journal Entry Request Generator
This generator produces correct NetSuite REST record API requests for the three transactions integrations create most: a sales order with item lines, a balanced journal entry, and an invoice transformed from a sales order. It also gives the sales order call in C# with HttpClient for .NET teams.
Fill in
Lowercase with hyphen for sandbox, for example 1234567-sb1.
OneWorld only. Remove the subsidiary line on non-OneWorld accounts.
1. Create a sales order
POST https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder
Authorization: Bearer <access_token>
Content-Type: application/json
X-NetSuite-PropertyNameValidation: Error
{
"externalId": "SO-EXT-1001",
"entity": { "id": "1234" },
"subsidiary": { "id": "1" },
"location": { "id": "2" },
"memo": "Created via REST API",
"item": {
"items": [
{
"item": { "id": "456" },
"quantity": 10,
"rate": 25.00
}
]
}
}
Success: HTTP 204 No Content
Location: https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder/<new internal id>2. Create a journal entry (sublist is line, not item)
POST https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/journalEntry
Authorization: Bearer <access_token>
Content-Type: application/json
X-NetSuite-PropertyNameValidation: Error
{
"subsidiary": { "id": "1" },
"memo": "Created via REST API",
"line": {
"items": [
{
"account": { "id": "112" },
"debit": 1500.00,
"memo": "Created via REST API"
},
{
"account": { "id": "54" },
"credit": 1500.00,
"memo": "Created via REST API"
}
]
}
}3. Transform the sales order into an invoice, or upsert by external ID
POST https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder/<sales order internal id>/!transform/invoice
Authorization: Bearer <access_token>
Content-Type: application/json
{}
Upsert by external ID instead of create (same body without externalId):
PUT https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder/eid:SO-EXT-10014. C# HttpClient: send request 1
// .NET 6 or later console app (top-level statements)
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var url = "https://1234567.suitetalk.api.netsuite.com/services/rest/record/v1/salesOrder";
var accessToken = Environment.GetEnvironmentVariable("NS_ACCESS_TOKEN");
var body = new
{
externalId = "SO-EXT-1001",
entity = new { id = "1234" },
subsidiary = new { id = "1" },
location = new { id = "2" },
memo = "Created via REST API",
item = new
{
items = new[]
{
new { item = new { id = "456" }, quantity = 10, rate = 25.00 }
}
}
};
using var http = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Add("X-NetSuite-PropertyNameValidation", "Error");
request.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");
using var response = await http.SendAsync(request);
if ((int)response.StatusCode == 204)
{
Console.WriteLine("Created: " + response.Headers.Location);
}
else
{
Console.WriteLine((int)response.StatusCode + " " + await response.Content.ReadAsStringAsync());
}Check every field ID against the REST API Browser for your account version (record v1, salesOrder and journalEntry), because mandatory custom fields, forms and features (OneWorld, multi-location, approvals) change what a create needs. A successful create or transform returns 204 No Content with the new record URL in the Location header. Keep X-NetSuite-PropertyNameValidation: Error while testing so misspelled fields fail instead of being ignored.
Get your NetSuite payloads checked
Send us your record types and required fields and we will review the JSON against your account's forms and features.
Optional. The tool and every export work without this. No spam.
How to use it
Look up the internal IDs you need first: customer, subsidiary, location, item and the two GL accounts. Enable Show Internal IDs under Home > Set Preferences to see them on lists. Enter them with quantity, rate and amount, then copy the block you need. Block 1 creates the sales order, block 2 a two-line journal, block 3 bills an existing order, and block 4 sends block 1 from C#. Reference fields are objects with an id, and sublists are objects with an items array, which is the shape the REST record service expects for both reads and writes.
- Sales order lines live under item.items.
- Journal entry lines live under line.items with debit or credit per line.
- Totals must balance or the journal is rejected.
Where this lives in NetSuite
All calls go to /services/rest/record/v1/<recordType> on the suitetalk.api.netsuite.com host for your account. The record type IDs are camel case in REST: salesOrder, journalEntry, invoice. The REST API Browser (linked from Oracle help, filtered by version) lists every field and sublist for each record, and it is the quickest way to confirm whether a custom body field such as custbody_ship_priority is writable. Transforms use POST /record/v1/salesOrder/{id}/!transform/invoice and accept a body that overrides fields on the new invoice; an empty {} bills everything that is billable. The role needs REST Web Services permission plus create rights on each transaction type.
Worked example and reading the response
With the defaults, block 1 creates an order for customer 1234 at location 2 with 10 units of item 456 at 25.00, so the line amount is 250.00 before tax. NetSuite answers 204 No Content and puts the new URL in the Location header, for example .../salesOrder/98765. Read the ID from that header, not from the body, because there is no body. Block 3 then bills order 98765 and again returns 204 with the invoice URL. If you send block 1 twice with the same externalId, the second create fails with a duplicate external ID error, which is why the PUT .../salesOrder/eid:SO-EXT-1001 upsert form is safer for retries.
- 204: created, read Location.
- 400: field or value problem, read o:errorDetails.
- 403: role or permission problem.
Common mistakes
Sending journal lines under item instead of line is the most common journal failure. Others: sending a reference as a bare number instead of {"id": "..."}, using a non-posting or inactive account, omitting subsidiary on OneWorld, and leaving a mandatory custom field empty because the form used by REST differs from the UI form. Amounts are numbers, not strings. Dates go in YYYY-MM-DD form. If an order lands in Pending Approval, that is your sales order approval preference, not the API. For high volume, reuse the token and respect the account concurrency limit rather than retrying in tight loops.
Frequently Asked Questions
How do I create a sales order with the NetSuite REST API?
POST to /services/rest/record/v1/salesOrder with entity as {"id": "<customer id>"} and lines under item.items, each with item, quantity and rate. Add subsidiary and location where your account requires them. A 204 response with a Location header containing the new internal ID means it worked.
What is the JSON for a journal entry in the NetSuite REST API?
POST to /record/v1/journalEntry with subsidiary and a line sublist: line.items holds one object per line with account as a reference and either debit or credit as a number. Debits and credits must balance. Add memo, department, class or location per line where your segments require them.
How do I upsert a record by external ID in NetSuite REST?
Use PUT on /record/v1/<recordType>/eid:<externalId> with the record body. If a record with that external ID exists it is updated, otherwise it is created with that external ID. This makes retries after a timeout safe, which a plain POST create is not.
Is there a C# example for the NetSuite REST API?
Block 4 is one: it builds the body as an anonymous object, serializes it with System.Text.Json, sends it with HttpClient and a Bearer token, and reads the Location header on a 204. Get the token with the OAuth 2.0 client credentials flow or sign each request with TBA if you are on an older integration.
If you want an assistant that drafts these transactions from emails or spreadsheets and posts them only after a named user approves, we can build it on your own infrastructure.
Related Tools
NetSuite REST API SuiteQL Request Generator
Turn a SuiteQL statement into a ready POST to the REST query/v1/suiteql endpoint, plus curl and a Python script that pages through every result.
Developer ToolsNetSuite OAuth 2.0 Client Credentials (M2M) JWT Code Generator
Generate the openssl commands, the signed JWT client assertion and the token request for NetSuite machine-to-machine OAuth 2.0, in Python and Node.js.
NetSuiteNetSuite REST Record API URL Builder
Generate correct REST web services URLs and sample calls for a NetSuite record, a record list, a SuiteQL query or the metadata catalog.
NetSuiteNetSuite Script and REST Error Codes Reference
Common NetSuite error names and HTTP status codes with the usual cause and the first thing to check.
NetSuiteNetSuite Internal ID Lookup Worksheet
An editable table to record internal IDs, script IDs and external IDs per environment, so scripts and integrations stop hardcoding guesses.
More Developer Tools tools
See all- NetSuite SuiteScript 2.1 RESTlet Skeleton Generator
- NetSuite SuiteScript Governance Units by Script Type
- NetSuite API Retry Backoff Wait Time Calculator
- NetSuite OAuth 2.0 Access Token Refresh Calculator
- NetSuite REST Web Services Headers and Query Parameters Reference
- SuiteScript 1.0 to 2.1 API Mapping Reference for NetSuite
- NetSuite Token-Based Auth OAuth 1.0 Signature Code Generator
- SuiteScript 2.1 Scheduled Script Generator for NetSuite
- SuiteScript 2.1 Client Script Generator for NetSuite
Keep exploring
Free tools
- NetSuite REST API SuiteQL Request GeneratorTurn a SuiteQL statement into a ready POST to the REST query/v1/suiteql endpoint, plus curl and a Python script that pages through every result.
- JD Edwards F0911Z1 Journal Entry Batch Input SQL GeneratorGenerate a balanced pair of F0911Z1 batch journal entry rows with implied decimals handled, a verification query and the processing steps for R09110Z.
- NetSuite SuiteQL Transaction Query GeneratorPick a transaction type, date range and row limit and get ready-to-run SuiteQL for headers, lines and the REST call that executes it.
ERP fixes and how-tos
- How to run intercompany transactions in NetSuite (OneWorld)Set up and process NetSuite intercompany sales orders, purchase orders and journal entries across subsidiaries, plus why elimination journals fail to balance.
- How to Import CSV Data Into NetSuite ReliablyPractical NetSuite CSV import guide: field mapping, internal ID matching, update vs create, handling sublists, and avoiding common import errors.
- Oracle Fusion General Ledger: Import Journals errors and rejected rowsFix Oracle Fusion General Ledger journal import errors: read GL_INTERFACE status, check source, ledger, period and account validity, then correct and reimport.
Runs with your ERP
- Sovos and Oracle NetSuite: sales tax for ERP and SuiteCommerceHow Sovos calculates sales tax in real time for NetSuite and SuiteCommerce through a SuiteTax-based integration, with setup steps, pitfalls and AI ideas.
- NetSuite Connector for ADP Workforce Now (First-Party)Oracle's first-party NetSuite connector for ADP Workforce Now syncs employees, payroll journals and paychecks, with Labor Cost Insights. Setup and pitfalls.
- Vertex and Oracle NetSuite: legacy tax, SuiteTax and SuiteCommerceHow Vertex integrates with NetSuite for sales, use and VAT tax: certified legacy tax, SuiteTax and SuiteCommerce options, setup steps, pitfalls and AI uses.
AI on your ERP
- NetSuite Month-End Close With AI That Finds the Problems Before the DeadlineAI for NetSuite month-end close: find unposted items, unusual journals, intercompany gaps and accrual candidates with SuiteQL, with controller review on every step.
- AI for the ERP Month-End Close: Variance Commentary Without the All-NighterAI for the ERP month-end close: draft variance commentary, flag journal entry anomalies, and speed audit support, grounded in the general ledger, reviewed by finance before anything posts.
Integrations
- How to automate email processing in Oracle NetSuiteAutomate inbound email in Oracle NetSuite: turn POs, RFQs, invoices and confirmations from shared inboxes into reviewed transactions using email capture and REST.
- How to add WhatsApp to Oracle NetSuiteHow to connect WhatsApp Business to Oracle NetSuite: SuiteScript triggers, RESTlets, template messages, opt-in rules and two-way replies that post back to records.