NetSuite: Reading and Writing Custom Fields
1. Overview
NetSuite allows users to create custom fields on records like invoices, bills, credit notes, customers, and more. The Apideck Unified API supports reading and writing these custom fields through the custom_fields property, so you can access NetSuite-specific data without leaving the unified model.
This guide covers how custom fields appear in API responses, how to write them back when creating or updating records, and how to find the correct field identifiers in your NetSuite account.
Supported resources: Invoices, Credit Notes, Vendor Payments (Bill Payments), Customer Payments, Sales Tax Items, and Employees.
2. Reading Custom Fields
When you retrieve a record that has custom fields, they are returned in the custom_fields array on the response object. Each entry contains an id (the NetSuite numeric internal ID) and a value. The script ID (e.g., custbody_shopify_payment_method) is not included at the root level — see line item custom fields below for where the script ID appears as name.
Example response (Invoice):
{
"id": "30773",
"number": "INV1",
"customer": { "id": "11266", "display_name": "669 Jill Farrell" },
"custom_fields": [
{ "id": "595", "value": "shopify_payments" },
{ "id": "394", "value": "false" }
]
}In this example, 595 is the internal ID for the custbody_shopify_payment_method field and 394 is the internal ID for custbody_shopify_cc_approved.
How values are parsed:
The connector attempts to JSON-parse each value. JSON-parseable values (numbers, booleans, objects) are returned as their native type. Everything else is returned as a string. Fields with empty values are excluded from the response.
Line item custom fields:
Custom fields on invoice line items are also supported. Each line item can include its own custom_fields array with id, name, and value properties. Line items include both the NetSuite internalId (as id) and the scriptId (as name), giving you both identifiers.
{
"line_items": [
{
"id": "1",
"description": "Consulting services",
"custom_fields": [
{
"id": "456",
"name": "custcol_department_code",
"value": "ENG"
}
]
}
]
}Tip: Use raw=true on your request to see the full downstream NetSuite response alongside the unified response. This helps identify exactly which custom fields are available on a record and shows both the internal ID and script ID for each field. See the Raw Mode guide for details.
3. Writing Custom Fields
When creating or updating a record, include the custom_fields array in the request body. Only id and value are needed for writes.
Example: Create an Invoice with Custom Fields
curl --request POST \
--url 'https://unify.apideck.com/accounting/invoices' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'x-apideck-consumer-id: YOUR_CONSUMER' \
--header 'x-apideck-app-id: YOUR_APP_ID' \
--header 'x-apideck-service-id: netsuite' \
--header 'Content-Type: application/json' \
--data '{
"customer": { "id": "11266" },
"invoice_date": "2023-01-05",
"number": "INV1",
"custom_fields": [
{ "id": "595", "value": "shopify_payments" },
{ "id": "394", "value": "false" }
]
}'You can also use script IDs instead of numeric internal IDs on writes:
{
"custom_fields": [
{ "id": "custbody_shopify_payment_method", "value": "shopify_payments" },
{ "id": "custbody_shopify_cc_approved", "value": "false" }
]
}4. Field Identifiers: Internal ID vs Script ID
You can reference custom fields using either the numeric internal ID or the string script ID when writing. The connector detects which one you're using automatically:
If the
idvalue is a number (e.g.,"id": "595"), it is treated as a NetSuite internalIdIf the
idvalue is a string (e.g.,"id": "custbody_shopify_payment_method"), it is treated as a NetSuite scriptId
Script IDs are generally recommended for writes because they are human-readable and consistent across NetSuite environments (sandbox vs. production), while internal IDs can differ between environments.
Note: This flexibility applies to write operations only. On read responses, the id is always the numeric NetSuite internal ID. To find the corresponding script ID for a field, use the NetSuite UI or SuiteQL query described in section 6, or use raw=true to inspect the full SOAP response.
5. Automatic Type Detection
When writing custom fields, you do not need to specify the NetSuite custom field type. The connector automatically detects the type based on the value you provide:
Value Example | Detected NetSuite Type |
| StringCustomFieldRef |
| DoubleCustomFieldRef |
| BooleanCustomFieldRef |
| DateCustomFieldRef |
Important: All values should be passed as strings. The connector handles the type conversion.
Note on Select and Multi-Select fields: NetSuite also supports SelectCustomFieldRef and MultiSelectCustomFieldRef types, which are not auto-detected. If you need to write to a select-type custom field, you can use the body-level pass_through mechanism to set the exact SOAP structure. See section 7 below.
6. Finding Your Custom Field IDs
NetSuite custom field identifiers can be found in several ways:
Option A - NetSuite UI: Go to Customization > Lists, Records, & Fields > Entity Fields (or Transaction Body Fields / Transaction Column Fields depending on the field type). The ID column shows the script ID (e.g., custbody_project_code), and the internal ID is shown in the Internal ID column (you may need to enable this column in View > Set Preferences).
Option B - SuiteQL via Proxy API: Query NetSuite's customfield table to list all custom fields:
curl --request POST \
--url 'https://unify.apideck.com/proxy' \
--header 'x-apideck-consumer-id: YOUR_CONSUMER' \
--header 'x-apideck-app-id: YOUR_APP_ID' \
--header 'x-apideck-service-id: netsuite' \
--header 'x-apideck-downstream-url: https://{account_id}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql' \
--header 'accept: application/json' \
--header 'prefer: transient' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--data '{ "q": "SELECT scriptid, fieldtype, label FROM customfield ORDER BY scriptid" }'This returns all custom fields with their script IDs, types, and labels.
Option C - Raw Mode: Make a GET request with raw=true to see the full NetSuite SOAP response, which includes the customFieldList with both internal IDs and script IDs for each field. This is the easiest way to match up the numeric id you see in the unified response with the human-readable script ID.
7. Advanced: Using Pass Through for Unsupported Field Types
For custom field types that are not auto-detected (like SelectCustomFieldRef or MultiSelectCustomFieldRef), you can use the body-level pass_through to inject the exact SOAP structure NetSuite expects.
Example: Writing a Select Custom Field
curl --request POST \
--url 'https://unify.apideck.com/accounting/invoices' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'x-apideck-consumer-id: YOUR_CONSUMER' \
--header 'x-apideck-app-id: YOUR_APP_ID' \
--header 'x-apideck-service-id: netsuite' \
--header 'Content-Type: application/json' \
--data '{
"customer": { "id": "11266" },
"invoice_date": "2023-01-05",
"line_items": [
{
"description": "Consulting",
"total_amount": 5000
}
],
"pass_through": [
{
"service_id": "netsuite",
"extend_object": {
"tranSales:customFieldList": {
"platformCore:customField": [
{
"_attributes": {
"internalId": "custbody_status",
"xsi:type": "platformCore:SelectCustomFieldRef"
},
"platformCore:value": {
"_attributes": {
"internalId": "2"
}
}
}
]
}
}
}
]
}'For more details on the pass_through body mechanism, see the Pass Through guide.
8. Querying Custom Fields via SuiteQL
You can also query custom field values using pass_through[q] with a SuiteQL query. Custom field script IDs can be used directly as column names in SuiteQL.
Example: Get invoices with a specific custom field value
GET https://unify.apideck.com/accounting/invoices?pass_through[q]=SELECT id, tranid, custbody_project_code FROM transaction WHERE type = 'CustInvc' AND custbody_project_code = 'PRJ-2024-100'&limit=200For more SuiteQL examples, see the pass_through parameters guide.
