# NetSuite — Accounting API: connection

> Canonical URL: https://developers.apideck.com/apis/accounting/netsuite/connection

## Connect to NetSuite

This guide will walk you through the process of setting up NetSuite User Roles to get Access Tokens using Token-Based Authentication. Access tokens are used to authenticate requests to NetSuite APIs, which enables you to integrate NetSuite with other applications.

By following these steps, you can grant specific NetSuite User Roles access to generate Access Tokens and use them to access NetSuite APIs securely.

NetSuite uses Token-Based Authentication (TBA), so there is **no authorize-and-redirect flow**: you create the access token yourself in the NetSuite UI and paste the credentials into your Apideck Vault connection.

**Steps: bundle path (recommended):**

1. [Install the Apideck Integration bundle](#recommended-install-the-apideck-integration-bundle)
2. [Assign the role to a new user](#assign-a-user-to-the-role)
3. [Create access tokens](#create-access-tokens): select **Apideck Unify** as Application and Role
4. [Find your Account ID](#find-your-account-id)
5. [Configure the connection](#configure-the-netsuite-connection): only **3 fields** needed (Account ID, Token ID, Token Secret)

**Steps: manual path (existing customers or restricted accounts):**

1. [Create the role manually](#alternative-create-the-role-manually)
2. [Assign the role to a new user](#assign-a-user-to-the-role)
3. [Create an application (consumer key & secret)](#create-an-application)
4. [Create access tokens](#create-access-tokens)
5. [Find your Account ID](#find-your-account-id)
6. [Configure the connection](#configure-the-netsuite-connection): all **5 fields** needed

Info: This guide assumes you have a basic understanding of NetSuite and its interface.

### Prerequisites

- **NetSuite Administrator access**: creating roles, integration records and access tokens, and installing a bundle, all require it.
- **The SuiteTalk web services and Token-Based Authentication features enabled** on the account, at **Setup → Company → Enable Features → SuiteCloud**: *SOAP Web Services*, *REST Web Services* and *Token-Based Authentication*. Both web services features are needed: Apideck validates the connection over SOAP, while filtered list calls run as SuiteQL over REST. See Oracle's [Token-Based Authentication guide](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_4247337262.html) and [SuiteTalk Web Services overview](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_1540391670.html).
- **Access to your Apideck Vault** to enter the credentials once they exist.
- On **OneWorld** accounts, decide whether this connection should read from **one** subsidiary or from all of them. That is the optional **Default Subsidiary** setting on the same Vault form (see [Optional connection settings](#optional-connection-settings)), not a separate connection, and you can change it later. See Oracle's [OneWorld multi-subsidiary documentation](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_N295957.html).

> NetSuite shows the Consumer Key/Secret and the Token ID/Secret **exactly once**, on the screen where they are created. Copy them straight away: a lost secret can only be regenerated, never retrieved.

### Recommended: install the Apideck Integration bundle

Apideck publishes a NetSuite SuiteBundle that ships a pre-built least-privilege
**Apideck Unify** role covering the core 39 permissions for the standard Apideck
accounting resources, plus an **Apideck Unify** Integration Record pre-configured for Token-Based Authentication. Installing the bundle takes about a minute and replaces the manual role build described in the "Alternative" section below.

Because that Integration Record already carries Apideck's Consumer Key and Secret, you never have to create or handle them; you supply only your own account-side credentials (Account ID, Token ID, Token Secret).

For the full step-by-step walkthrough with screenshots, see the dedicated **[Apideck Integration Connectivity Bundle Setup Guide](https://developers.apideck.com/connectors/netsuite/docs/consumer+connection-bundle-setup)**.

**Quick summary:**

1. Go to **Customization → SuiteBundler → Search & Install Bundles**, search for bundle ID **705521** and click **Install**.
2. Create a dedicated integration user and assign the **Apideck Unify** role to them.
3. Create an Access Token: select **Apideck Unify** as both the Application and Role.
4. In Vault, enter only **3 fields**: Account ID, Token ID, Token Secret. Leave Consumer Key and Consumer Secret blank.

The full list of the 39 permissions installed by the bundle, and the permissions some operations and NetSuite features need you to add to the role by hand, is in the **[Bundle Setup Guide](https://developers.apideck.com/connectors/netsuite/docs/consumer+connection-bundle-setup)**.

> The bundle is **unmanaged**, so once installed you can inspect the role at
> **Setup → Users/Roles → Manage Roles** and add extra permissions if your
> account uses features outside the standard Apideck accounting scope.
>
> **Bundle updates:** When Apideck publishes a new bundle version, apply it from
> **Customization → SuiteBundler → Installed Bundles**. New permissions are added
> automatically. Any permissions you added manually to the role, such as bank feed
> permissions or Tax Details Tab for SuiteTax (UK, EU, and other VAT regions), **will be removed** by the update
> and must be re-added manually afterwards.

### Alternative: create the role manually

Use this path only if your NetSuite account policy blocks installing
unmanaged bundles, or if you need permissions beyond what the bundle ships.
The bundle is the recommended default. See the previous section.

On this path you also create your own Integration Record, so the connection needs all **5** credentials (Account ID, Consumer Key, Consumer Secret, Token ID, Token Secret).

To grant access to the Netsuite, you need to create a user role to get Access Tokens using Token-Based Authentication

1. Log in to your NetSuite account as an administrator.

2. Go to **Setup** > **Users/Roles** > **Manage Roles**.![netsuite new role](https://res.cloudinary.com/apideck/image/upload/v1679311319/docs/connectors/netsuite/netsuite-new-role.png)

3. Click on the 'New' button to create a new role.
   ![netsuite role form](https://res.cloudinary.com/apideck/image/upload/v1679312282/docs/connectors/netsuite/netsuite-role-form.png)Give the role a name (e.g. _Integration Role_).

4. Under the 'Permissions' tab, select the appropriate permissions that you want to grant to this role.
   ![netsuite role permissions](https://res.cloudinary.com/apideck/image/upload/v1679312282/docs/connectors/netsuite/netsuite-role-permissions.png)Note that the role must have the necessary permissions to perform the tasks that the integration will be used for.

   We recommend to review the permission levels for the various sections:

   For "Transactions","Lists" we recommend to set the permission to "View" to allow reading data and "Full" to allow creating and/or modifying data.
   For "Lists","Currencies" we recommend to set the permission to "View" to allow reading data and "Full" to allow creating and/or modifying data.
   For "Setup", the only option is "None" or "Full", please set it to "Full" to grant access to the Netsuite instance.

   For more details about the Netsuite permission levels, please refer to the [Access Levels page](https://system.netsuite.com/app/help/helpcenter.nl?fid=section_1550000000.html) in NetSuite Help Center.

   **Important for Projects Functionality**: To enable customer-to-project mapping and full project management features, ensure the following permissions are granted:

   - **Lists > Projects**: Required for accessing job/project records
   - **Lists > Customers**: Required for customer-project relationship mapping

   **Important for Financial Reports (Profit & Loss, Balance Sheet)**: These reports use the SOAP `getPostingTransactionSummary` API which only requires "Financial Statements" permission. Ensure the following permission is granted:

   - **Reports > Financial Statements**: View access required for financial report queries
   - **Lists > Accounts**: View access for chart of accounts data (used for account name enrichment)

   <a id="suiteql-access"></a>
   **Important for filtered list calls (SuiteQL access)**: any list call with a `filter[*]` parameter (e.g. `filter[updated_since]`, `filter[id_since]`) on `invoices`, `bills`, or `credit-notes` is routed through NetSuite's REST/SuiteQL endpoint instead of SOAP. SuiteQL has its own access checks on every table touched by the query (the `transaction` table plus the `customer` JOIN), so on top of the standard Setup permissions the role must include:

   | PERMISSION                          | AREA          | LEVEL |
   | ----------------------------------- | ------------- | ----- |
   | SuiteAnalytics Workbook             | Reports       | Edit  |
   | Find Transaction                    | Transactions  | View  |
   | Invoice / Bill / Credit Memo        | Transactions  | View (per resource used) |
   | Customers                           | Lists         | View  |

   The address subrecords joined into the invoice list (`transactionBillingAddress`, `transactionShippingAddress`) inherit access from the parent `transaction` record: there is **no separate `Lists > Address` permission** to grant.

   If a permission is missing, NetSuite does not say which one in a single response. Each missing permission produces a distinct symptom:

   | What's missing                          | Symptom on filtered list calls |
   | --------------------------------------- | ------------------------------ |
   | SuiteAnalytics Workbook                 | `400 Bad Request: Invalid search query. Your current role does not have permission to perform this action.` |
   | Find Transaction                        | `400 Bad Request: Invalid search query. Search error occurred: Record 'transaction' was not found.` |
   | Customers                               | `400 Bad Request: Invalid search query. Search error occurred: Record 'customer' was not found.` |
   | Invoice / Bill / Credit Memo (per type) | `200 OK` with `data: []`: query succeeds but every row of the corresponding type is silently filtered out, so the integration looks healthy while returning nothing |

   The Setup permissions `REST Web Services`, `SOAP Web Services` and `Log in using Access Tokens` are needed for **any** Apideck connection (Apideck validates the connection through a SOAP call before any SuiteQL request runs); they are listed in the Setup table further below. Non-filter list calls and `GET /accounting/{resource}/{id}` calls go through SOAP and don't require SuiteAnalytics Workbook or Find Transaction.

   The `expenses` list (`GET /accounting/expenses`) is the exception: it always goes through SuiteQL, with or without `filter[updated_since]`, so it needs SuiteAnalytics Workbook, Find Transaction and `Transactions > Credit Card` (View) even for a plain list call.

   See [Syncing large NetSuite transaction datasets](https://developers.apideck.com/connectors/netsuite/docs/application_owner+large_transaction_datasets) for why filtered list calls are the recommended path for large datasets.

   **Transactions**

   | TRANSACTIONS PERMISSION                 | LEVEL     |
   | --------------------------------------- | --------- |
   | Access Payment Audit Log                | View/Full |
   | Audit Trail                             | View/Full |
   | Automated Cash Application              | View/Full |
   | Bill Purchase Orders                    | View/Full |
   | Bills                                   | View/Full |
   | Calculate Time                          | View/Full |
   | Cash Sale                               | View/Full |
   | Cash Sale Refund                        | View/Full |
   | Check                                   | View/Full |
   | Credit Card                             | View/Full |
   | Credit Card Refund                      | View/Full |
   | Credit Memo                             | View/Full |
   | Credit Returns                          | View/Full |
   | Customer Deposit                        | View/Full |
   | Customer Payment                        | View/Full |
   | Customer Refund                         | View/Full |
   | Deposit                                 | View/Full |
   | Deposit Application                     | View/Full |
   | Edit Forecast                           | View/Full |
   | Enter Opening Balances                  | View/Full |
   | Enter Vendor Credits                    | View/Full |
   | Estimate                                | View/Full |
   | Expense Report                          | View/Full |
   | Finance Charge                          | View/Full |
   | Find Transaction                        | View/Full |
   | Fulfill Orders                          | View/Full |
   | Generate Price Lists                    | View/Full |
   | Generate Statements                     | View/Full |
   | Import Online Banking File              | View/Full |
   | Invoice                                 | View/Full |
   | Invoice Approval                        | View/Full |
   | Invoice Sales Orders                    | View/Full |
   | Item Fulfillment                        | View/Full |
   | Item Receipt                            | View/Full |
   | Make Journal Entry                      | View/Full |
   | Matching Rules for Online Banking       | View/Full |
   | Opportunity                             | View/Full |
   | Pay Bills                               | View/Full |
   | Payments                                | View/Full |
   | Pay Sales Tax                           | View/Full |
   | Post Vendor Bill Variances              | View/Full |
   | Posting Period on Transactions          | View/Full |
   | Purchase Order                          | View/Full |
   | Receive Order                           | View/Full |
   | Receive Returns                         | View/Full |
   | Reconcile                               | Edit      |
   | Refund Returns                          | View/Full |
   | Return Auth. Approval                   | View/Full |
   | Return Authorization                    | View/Full |
   | Sales Order                             | View/Full |
   | Sales Order Approval                    | View/Full |
   | Set Up Budgets                          | View/Full |
   | Statement Charge                        | View/Full |
   | System Journal                          | View/Full |
   | Timer                                   | View/Full |
   | Time Entry                              | View/Full |
   | Time Tracking                           | View/Full |
   | Track Time                              | View/Full |
   | Transfer Funds                          | View/Full |
   | Vendor Bill Approval                    | View/Full |
   | Vendor Payment Approval                 | View/Full |
   | Vendor Return Auth. Approval            | View/Full |
   | Vendor Return Authorization             | View/Full |
   | Vendor Returns                          | View/Full |
   | View Gateway Asynchronous Notifications | View/Full |
   | View Payment Events                     | View/Full |

   **Reports**

   | REPORTS PERMISSIONS     | LEVEL |
   | ----------------------- | ----- |
   | SuiteAnalytics Workbook | Edit  |
   | Financial Statements    | View  |

   **List**

   | LISTS PERMISSIONS   | LEVEL     |
   | ------------------- | --------- |
   | Accounts            | View/Full |
   | Classes             | View/Full |
   | Companies           | View/Full |
   | Contacts            | View/Full |
   | Currency            | View/Full |
   | Customers           | View/Full |
   | Documents and Files | Create (or Full) |
   | Departments         | View/Full |
   | Employee Records    | View/Full |
   | Expense Categories  | View/Full |
   | Items               | View/Full |
   | Locations           | View/Full |
   | Projects            | View/Full |
   | Sales Tax Items     | Full: SuiteTax-enabled accounts (tax-rate reads) |
   | Subsidiaries        | View/Full |
   | Tax Details Tab     | Full      |
   | Tax Records         | View/Full |
   | Vendors             | View/Full |

   > **Important for Invoice Items writes**: The `Lists → Currency` permission (View) is required for `POST /accounting/invoice-items` and `PATCH /accounting/invoice-items/{id}`. The connector resolves the `currency` ISO code to a NetSuite internal ID via an internal `currenciesAll` call during every write. Without this permission that call is denied, the currency cannot be resolved, and the write returns a `400` error with a message indicating the currency could not be resolved. Grant **View** access at minimum; **Full** is required if you also create or modify currencies.
   >
   > If you cannot grant this permission, supply the NetSuite internal currency id directly via pass_through, which bypasses the lookup entirely:
   > ```json
   > "pass_through": [{ "service_id": "netsuite", "extend_object": { "currency_id": "1" } }]
   > ```
   > You can set any other record field the same way using `listAcct:`-prefixed keys in compact-XML shape (e.g. `"listAcct:isTaxable": { "_text": "true" }`). Any other `extend_object` key (e.g. a bare `currency`) is rejected with a clear error, because for SOAP connectors it would serialize outside the request envelope and break the request: use `currency_id`, a `listAcct:` key, or `pass_through.extend_paths` with a full JSONPath instead.

   <a id="tax-details-tab"></a>
   > **Important for per-line tax on expenses (SuiteTax)**: The `Lists → Tax Details Tab` permission is required for `POST /accounting/expenses` and `PATCH /accounting/expenses/{id}` on Credit Card Charge expenses whenever a line carries `line_items[].tax_rate` **and** the connection has SuiteTax enabled (the **SuiteTax enabled** connection setting, see [Optional connection settings](#optional-connection-settings)).
   >
   > Card-charge expenses post to a NetSuite Credit Card Charge, which is a SuiteTax-only record (NetSuite Checks expose no tax fields through the API, so tax on a check is rejected regardless of this permission). The legacy per-line `taxCode` field is a no-op there, so the connector applies each line's tax code by writing a **tax-details override** block on the record instead. NetSuite's tax engine refuses that block unless the role holds this permission.
   >
   > Without it the write fails as a *tax-engine* error rather than a permission error, so the message does not obviously point at a role setting:
   >
   > ```
   > Unable to save the transaction due to an error being reported by the tax calculation
   > engine: A User Error Has Occurred: Permission Violation: You need the
   > 'Lists -> Tax Details Tab' permission to access this page.
   > ```
   >
   > Grant **Full**. Unlike most `Lists` permissions this is not a `View`/`Full` pair: it is only consulted when *writing* the tax-details block, so `View` adds nothing: reading an expense's per-line `tax_rate` works without this permission at all. `Full` is also the level confirmed working in practice.
   >
   > This is separate from `Lists → Tax Records`, which covers the tax code and tax type records themselves; both are needed.
   >
   > If you are narrowing an existing role's permissions, keep this one. Removing it does not surface until an expense is created with per-line tax rates, and the failure above gives no hint that a permission was removed.
   >
   > Each line must also send both `tax_rate.id` and `tax_rate.rate` for per-line tax to apply. See the [expenses gotchas](https://developers.apideck.com/apis/accounting/netsuite/gotchas).

   **Setup**

   | SETUP PERMISSIONS          | LEVEL |
   | -------------------------- | ----- |
   | Accounting Lists           | Full  |
   | Accounting Management      | Full  |
   | Deleted Records            | Full  |
   | Log in using Access Tokens | Full  |
   | Other Lists                | Full  |
   | REST Web Services          | Full  |
   | SOAP Web Services          | Full  |

   > **Note**: Financial reports (Profit & Loss, Balance Sheet) use the SOAP `getPostingTransactionSummary` API which only requires the **Financial Statements** permission under Reports. This is a less restrictive permission than SuiteQL which requires "SuiteAnalytics Workbook". Without Financial Statements permission, these endpoints will return 401 Unauthorized errors.

### Assign a user to the role

It is recommended that you create a separate user for this purpose instead of assigning the role to an existing user. It helps with better tracking and auditing operations.

1. Click **Lists > Employees > New**

2. Enter the employee details (e.g. _Integration User_), and email address.
   ![netsuite employees new](https://res.cloudinary.com/apideck/image/upload/v1679329502/docs/connectors/netsuite/netsuite-employees-new.png)

3. Click **Access** tab
   ![netsuite employees access](https://res.cloudinary.com/apideck/image/upload/v1679332151/docs/connectors/netsuite/netsuite-employees-access.png)

   - select **Give Access**.

   - Enable **Manually Assign or Change Password** and specify a password.

4. Under **Roles**, select the appropriate role from the dropdown and click **Add**.
   - **Bundle path**: select **Apideck Unify** (installed by the bundle)
   - **Manual path**: select the role you created (e.g. _Integration Role_)

   ![netsuite employees roles](https://res.cloudinary.com/apideck/image/upload/v1679330267/docs/connectors/netsuite/netsuite-employees-roles.png)

### Create an Application

> **Bundle path users: skip this section.** The bundle installs the Apideck Unify Integration Record which provides the Consumer Key and Secret automatically. You do not need to create your own application. Continue to [Create access tokens](#create-access-tokens).

Once the role is set up, you can follow these steps to generate the consumer Key & Secret:

1. Log in to your NetSuite account as an administrator: creating an Integration Record requires Administrator access, not the integration role you just created.
2. Go to **Setup** > **Integrations** > **Manage integrations**.
   <img src="https://res.cloudinary.com/apideck/image/upload/v1679314111/docs/connectors/netsuite/netsuite-integrations-new.png" title="" alt="" width="445" />
3. Click the 'New' button to create a new integration or select an existing integration that you want to use.
   ![netsuite integrations form](https://res.cloudinary.com/apideck/image/upload/v1679314111/docs/connectors/netsuite/netsuite-integrations-form.png)

   - **Name**: Enter a meaningful name (for example, _Integration App_)
   - **Authentication**: Under the 'Authentication' section,
     - Check '**Token-Based Authentication**'
     - Check '**TBA: Authorization Flow**' (leave Callback URL blank)
   - **OAuth 2.0**: Leave all OAuth 2.0 options unchecked: Apideck uses Token-Based Authentication only

   Click the 'Save' button to save the changes.

4. Once saved, the Consumer Key and Consumer Secret will be generated.

   **💡 REMARK**: Copy the "Consumer Key" and the "Consumer secret". You can not access this information once you exit this screen.![netsuite integrations consumer id secret](https://res.cloudinary.com/apideck/image/upload/v1679324954/docs/connectors/netsuite/netsuite-integrations-consumer-id-secret.png)

### Create Access tokens

Create a New Access Token with the Role just created.

1. Go to **Setup** > **Users/Roles** > **Access Tokens**.

<img title="" src="https://res.cloudinary.com/apideck/image/upload/v1679316308/docs/connectors/netsuite/netsuite-access-token-navigation.png" alt="" width="342" />

2. Click the 'New' button to create a new Access token

3. Complete the form

   ![netsuite access token form](https://res.cloudinary.com/apideck/image/upload/v1679316421/docs/connectors/netsuite/netsuite-access-token-form.png)

   - Select the "**Application name**":
     - **Bundle path**: select **Apideck Unify** (installed by the bundle)
     - **Manual path**: select the application you created in the previous step (e.g. _Integration App_)

   - Select the "**User**", which we created in the previous steps (e.g. _Integration User_)

   - Select the "**Role**":
     - **Bundle path**: select **Apideck Unify**
     - **Manual path**: select the role you created (e.g. _Integration Role_)

   - Give the token a recognizable "**token name**", for example _Integration Token_

   - Click the 'Save' button to save the changes.

4. Once saved, the Account **Token ID** and **Secret** will be generated.

**💡 REMARK**: Copy the "Token Id" and the "Token secret". You can not access this information once you exit this screen.![netsuite access token id secret](https://res.cloudinary.com/apideck/image/upload/v1679320661/docs/connectors/netsuite/netsuite-access-token-id-secret.png)

### Find your Account ID

1. Go to **Setup** > **Company** > **Company Information**

2. Copy the Account ID

![netsuite account id](https://res.cloudinary.com/apideck/image/upload/v1698413817/docs/connectors/netsuite/netsuite-account-id.png)

The Account ID alone determines which NetSuite instance the connection reaches: sandbox account IDs carry a suffix such as `_SB1`, `_SB2` or `_SB3`.

### Configure the Netsuite connection

**Bundle path**: only 3 fields are required:

| Field | Where to find it |
|---|---|
| Account ID | Setup → Company → Company Information |
| Token ID | Generated when you created the access token |
| Token Secret | Generated when you created the access token |

Leave **Consumer Key** and **Consumer Secret** blank: Apideck fills these automatically from the Apideck Unify Integration Record installed by the bundle.

**Manual path**: all 5 fields are required:

| Field | Where to find it |
|---|---|
| Account ID | Setup → Company → Company Information |
| Consumer Key | Generated when you saved the Integration Record |
| Consumer Secret | Generated when you saved the Integration Record |
| Token ID | Generated when you created the access token |
| Token Secret | Generated when you created the access token |

In the Vault form the two token fields are labelled **Access token ID** and **Access token secret**: these are the NetSuite Token ID and Token Secret from the previous step.

<img title="" src="https://res.cloudinary.com/apideck/image/upload/v1679330811/docs/connectors/netsuite/apideck-vault-netsuite.png" alt="" data-align="center" />

Saving the connection triggers a validation call to NetSuite (a SOAP `getServerTime` request), which is why the `SOAP Web Services` and `Log in using Access Tokens` permissions are required even for a REST-heavy integration.

<a id="optional-connection-settings"></a>
### Optional connection settings

These fields sit alongside the credentials on the same Vault connection form. All are optional, but the first one is required for correct tax data on SuiteTax accounts.

| Setting | When you need it |
|---|---|
| **SuiteTax enabled** | Set to **Yes** if the SuiteTax feature is enabled on your NetSuite account. Tax-rate reads are then served from NetSuite's sales tax items, which is where SuiteTax accounts hold that data. Leaving it unset on a SuiteTax account returns tax data from the legacy tax-code records and will not match what you see in NetSuite. Accounts on legacy tax can leave it blank or set it to **No**. |
| **Default Subsidiary** | **NetSuite OneWorld accounts only.** Scopes list requests and the three financial reports (Profit & Loss, Balance Sheet, Trial Balance) to a single subsidiary, so callers do not have to pass `filter[subsidiary_id]` on every request. The dropdown is populated live from your own NetSuite account, so **fill in the account and token fields and save the connection first**: until the connection can reach NetSuite there is nothing to choose from. Leave it on **All subsidiaries** (the default) to return records across every subsidiary; that is also the right choice on a single-entity account, where the setting has no effect. **Selecting a parent subsidiary does not include its children**: NetSuite matches the subsidiary exactly, so a group-level integration that picks the parent sees only the parent's own records. |
| **Integration user time zone** | Optional IANA zone (e.g. `America/Los_Angeles`) for the NetSuite user this connection authenticates as. Find it at **Home → Set Preferences → Time Zone**. NetSuite stores date-only fields (invoice, bill and transaction dates) by converting the value into *that user's* zone and keeping the date part. Leave it blank unless the integration user is on Midway, Niue, American Samoa (UTC−11) or Kiritimati (UTC+14); dates land on the intended calendar day without configuration for any user between UTC−10 and UTC+13. If you do set it, keep it within 12 hours of the user's real zone in either direction. |
| **Attachments folder ID** | The internal ID of the File Cabinet folder that attachment uploads default to when a request does not pass `parent_folder_id`. NetSuite has no folderless files, so set this if your integration does not send a folder per request. Find the ID under **Documents → Files → File Cabinet** (the `folder=NNN` value in the folder's URL). |
| **Bank Feed RESTlet Script ID / Deploy ID** | Only for the optional bank feeds flow. See [Bank Feeds](#bank-feeds-optional) below. |

If you also use SuiteTax and write per-line tax on expenses, add the `Lists → Tax Details Tab` permission to the role. See [the Tax Details Tab note](#tax-details-tab). Accounts with **SuiteTax enabled** also need `Lists → Sales Tax Items` to read tax rates, because that setting routes tax-rate reads to NetSuite's sales tax item records.

### Notes

- **Credential ownership.** On the bundle path Apideck supplies the Consumer Key/Secret through the installed **Apideck Unify** Integration Record, so those two fields stay blank in Vault; everything else (Account ID, Token ID, Token Secret) is always yours and never leaves your NetSuite account except into the Vault form. On the manual path all five values are yours.
- **Secrets are shown once.** NetSuite displays the Consumer Key/Secret and Token ID/Secret only on the creation screen. If a value is lost, create a new access token (or a new Integration Record) and update the connection; it cannot be read back.
- **Access tokens do not expire on their own.** The connection keeps working until someone regenerates the token, revokes it, or deletes the Integration Record in NetSuite, at which point the connection stops authenticating and needs new credentials.
- **Sandbox refreshes reset the setup.** Refreshing a NetSuite sandbox does not carry the Integration Record and access token across. Recreate both afterwards and paste the new Token ID/Secret into the connection. This is expected NetSuite behaviour, not a connector fault.
- **OneWorld accounts: subsidiary scope is a setting, not a separate connection.** One connection can serve the whole account. The **Default Subsidiary** setting scopes list requests to one subsidiary; **All subsidiaries** (the default) returns records from every one. When an integration needs more than one entity from a single connection, two per-request overrides are available: `filter[subsidiary_id]` on the request, and the `x-apideck-company-id` header. An explicit `filter[subsidiary_id]` wins over the header, which wins over the connection setting. Subsidiary IDs come from `GET /accounting/companies`, which always lists every subsidiary in the account regardless of the setting. On single-entity accounts none of this applies and `filter[subsidiary_id]` is ignored.
- **What subsidiary scope does not cover.** `company-info` always resolves to the account's root subsidiary, whatever the setting says. **Writes are never scoped**: the subsidiary of a created or updated record comes from the request body, or is derived by NetSuite from the record's customer or vendor, so a scoped connection can create a record its own list requests will not return. Account-level data (currencies, payment methods, accounting periods, subsidiaries, attachments) is always returned in full, and a few resources have their own exceptions. See the [NetSuite gotchas](https://developers.apideck.com/apis/accounting/netsuite/gotchas).
- **Changing the Default Subsidiary mid-pagination.** A pagination cursor carries the scope it was created under, so continuing an older cursor after changing the setting keeps returning the previous scope. Restart the list after changing it.
- **Custom forms can block writes.** A custom field marked mandatory on a standard NetSuite form makes API writes to that record type fail, including creates that worked before the form changed. If writes to one record type start failing while others are fine, check that record type's form for new mandatory fields.

### Troubleshooting

**The connection will not validate after saving**

- Confirm the role assigned to the token's user has `SOAP Web Services`, `REST Web Services` and `Log in using Access Tokens` under Setup: the validation call is a SOAP request.
- Confirm the Access Token was created with the right Application *and* Role (on the bundle path both are **Apideck Unify**), and that the role is assigned to the integration user.
- Re-check the Account ID exactly as shown in Company Information, including any `_SB1` sandbox suffix.

**A list endpoint returns `200 OK` with an empty `data` array**

This is the silent-permission failure described under [SuiteQL access](#suiteql-access): a missing per-resource transaction permission (Invoice, Bill, Credit Memo, …) filters every row out instead of returning an error. Compare the role against the permission tables above before investigating the data itself.

**Profit & Loss or Balance Sheet returns 401**

Grant `Reports → Financial Statements` (View) to the role.

**Profit & Loss or Balance Sheet returns every figure as zero**

If the **Default Subsidiary** on the connection names a subsidiary that no longer exists, both reports return `200 OK` with all figures at zero rather than an error, indistinguishable from a genuinely inactive entity. Re-open the setting and pick a subsidiary that is still in the list (or **All subsidiaries**).

**The Default Subsidiary dropdown is empty**

The list is loaded live from your NetSuite account, so it only fills in once the connection can authenticate: enter Account ID, Token ID and Token Secret (plus Consumer Key and Secret on the manual path), save the connection, then re-open the form. If it is still empty, confirm the role has `Lists → Subsidiaries` (View or Full), and note the setting applies to OneWorld accounts only.

**A scoped connection returns fewer records than expected**

NetSuite matches the selected subsidiary exactly, so a parent subsidiary does not bring in its children. Either select **All subsidiaries** and scope per request with `x-apideck-company-id` or `filter[subsidiary_id]`, or keep one entity per connection if that suits your integration better. Reference-data lists behave differently: on departments, locations, tracking categories and expense categories, a record marked *include children* in NetSuite is returned for every descendant subsidiary.

**Tax rates do not match what NetSuite shows**

If SuiteTax is enabled on the account, set the **SuiteTax enabled** connection setting to **Yes**. See [Optional connection settings](#optional-connection-settings). With that setting on, the role also needs `Lists → Sales Tax Items`, since tax-rate reads then come from the sales tax item records.

**An attachment upload is rejected for a missing folder**

Either send `parent_folder_id` on the request or set the **Attachments folder ID** connection setting.

Still stuck? Contact [Apideck Support](https://help.apideck.com/en/articles/7774658).

### Bank Feeds (optional)

If you want to push bank statements into NetSuite's **Match Bank Data** workflow through the Unify `bank-feed-statements` endpoint, a small amount of extra setup is needed on top of the standard connection above.

**At a glance:**

1. **Install the Apideck Bank Feed bundle** in your NetSuite account. Your Apideck contact will share the bundle ID and installation instructions.

2. **Install the NetSuite Bank Statement Parsers SuiteApp** (bundle ID `293699`, published by Oracle NetSuite, free). This provides the CSV parser that the Apideck bundle feeds into. Install it from **Customization → SuiteBundler → Search & Install Bundles**.

3. **Create a Format Profile** (Setup → Accounting → Financial Institution → Format Profiles) linking the Apideck connectivity plug-in to the NetSuite bank accounts you want to feed.

4. **Provide two extra connection fields** in the Apideck Vault for your NetSuite connection:

   - **Bank Feed RESTlet Script ID**
   - **Bank Feed RESTlet Deploy ID**

   You can find both under **Customization → Scripting → Scripts → Apideck Bank Feed RESTlet**, on the *Deployments* tab. Each row's *External URL* contains `script=<numeric_id>` and `deploy=<numeric_id>`: those are the values to paste into the Vault.

   While either field is blank, every bank-feed request on that connection is rejected with HTTP 422 and an error that names the missing field; nothing is sent to NetSuite. Fill in both values and retry.

> The full walkthrough, including the **extra role permissions you must add to the Apideck Unify role**, Format Profile field mapping and formatting preferences, account linking, day-to-day operations and troubleshooting, is in the dedicated **[Apideck Bank Feed for NetSuite setup guide](https://developers.apideck.com/guides/bank-feeds-netsuite)** (see Prerequisites → Role permissions). You only need that guide if you intend to use the bank-feed-statements endpoint; regular NetSuite integrations don't require any of these steps.


[API reference](https://developers.apideck.com/apis/accounting/reference.md) · [Coverage matrix](https://developers.apideck.com/apis/accounting/coverage)
