> Documentation index: https://taggalongtech.github.io/continuous-control/llms.txt

# Install the NetSuite components

Human page: https://taggalongtech.github.io/continuous-control/install-netsuite.html

The NetSuite half — the RC custom records (RC = Revenue Cloud; the RC records are the Salesforce Revenue Cloud order graph mirrored into NetSuite), the revenue engine, and the sync endpoint — ships as a versioned release zip with a one-command installer. One download, one script, and the rest of setup happens from the Salesforce Connection wizard. Budget 30–45 minutes, most of it one-time auth setup.

> **The connection's writes and reads go through one endpoint,** the **Continuous Control (RESTlet)** script, `customscript_cc_control_rl`, deployment `customdeploy_cc_control_rl`. Salesforce's revenue engine also calls the Update POBs and Update Revenue Plans Suitelets directly after a sync, alongside the RESTlet — see [The engine Suitelets](https://taggalongtech.github.io/continuous-control/engine-suitelets.html). That's why those two Suitelet deployments stay open to all roles, the same as the RESTlet.

## Before you install
- **Administrator access** to the target NetSuite account.
- The [required features](https://taggalongtech.github.io/continuous-control/prerequisites.html) enabled (SuiteCloud scripting, SDF, Token-Based Authentication, REST Web Services, OAuth 2.0; Accounting, A/R and Sales Orders on the Accounting tab). **Subsidiaries (OneWorld) and Multiple Currencies are required** — every synced record needs a subsidiary and a currency. NetSuite Advanced Billing is not required; Continuous Control ships its own billing schedule records and never touches the native ones. No NetSuite revenue feature is required. The deploy manifest also lists **Inventory** as an optional feature — it is not required, and it exists only so the item-level Source Org field also covers inventory items on accounts that sell them; non-inventory, service, and other-charge items carry the same field either way.
- A workstation with **Node.js 18+** and the release zip. The current release is **ns-v3.3.1**: download [`continuous-control-netsuite-v3.3.1.zip`](https://taggalongtech.github.io/continuous-control/downloads/continuous-control-netsuite-v3.3.1.zip) (it pairs with Salesforce packages 3.3.0, 3.4.0 and 3.4.1; see [Versions & compatibility](https://taggalongtech.github.io/continuous-control/ref-versions.html)).

## Create the install-time credentials

The installer authenticates with OAuth 2.0 client credentials — a certificate you generate and register once. (This is separate from the runtime token Salesforce will use; that comes below.)
1. In the unzipped release folder, generate the pair:

```
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 730 -nodes \
  -keyout continuous-key.pem -out continuous-cert.pem \
  -subj "/CN=Continuous Control Installer"
```

   Keep `continuous-key.pem` private — it never leaves your machine.
2. In NetSuite: **Setup → Integration → OAuth 2.0 Client Credentials (M2M) Setup → Create New**. Entity: you (or an integration user); Role: Administrator or any role with the SuiteCloud Development Framework permission; Application: **SuiteCloud Development Integration**; Certificate: upload `continuous-cert.pem`.
3. Copy the **Certificate ID** NetSuite displays.

## Run the installer

```
NS_ACCOUNT=<your account id> \
NS_CERT_ID=<certificate id> \
NS_KEY_PATH=./continuous-key.pem \
./install.sh
```

The script authenticates, **validates the whole project against your account** (nothing deploys if validation fails — a missing feature is the usual cause), asks for confirmation, and deploys. `./install.sh --validate-only` is a dry run.

> **Always safe to re-run.** The deploy upserts components by their script ids and never deletes — a re-run or upgrade cannot damage existing data or configuration.

## Runtime credentials for Salesforce

Salesforce calls NetSuite with Token-Based Authentication. Three one-time steps:
1. **Setup → Integration → Manage Integrations → New** — name it `Continuous Control`, check **Token-Based Authentication** (leave the TBA authorization flow and user credentials unchecked), save, and copy the **Consumer Key / Consumer Secret** (shown once). **Under OAuth 2.0, uncheck Authorization Code Grant** — NetSuite pre-checks it, and leaving it on fails with *Invalid Redirect URI*. Only Token-Based Authentication should be checked.
2. Assign the **Continuous Control Integration** role (`customrole_cc_integration`) to the integration user — **Setup → Users/Roles → Manage Users**, open the user, add the role on the Access tab, save. The installer ships this role as an SDF object; do not hand-edit it in the NetSuite UI — `install.sh` re-applies it on every deploy and overwrites changes made there.

   Its 27 permissions, exactly as the role XML carries them:

   | Permission key | Level | What it's for |
   |---|---|---|
   | `ADMI_LOGIN_OAUTH` | Full | Log in with an access token |
   | `ADMI_RESTWEBSERVICES` | Full | Call REST web services (the RESTlet) |
   | `LIST_CUSTRECORDENTRY` | Full | Every `customrecord_cc_*` record — the RC custom records |
   | `ADMI_CUSTRECORD` | View | Read custom record type metadata (`describe`, `listrecordtypes`) |
   | `ADMI_CUSTLIST` | View | Read custom list values |
   | `ADMI_CUSTOMSCRIPT` | View | Read script records — never edit or upload one |
   | `LIST_FIND` | Full | Run searches and SuiteQL queries |
   | `LIST_PUBLISHSEARCH` | Create | Build the `customsearch_cc_*` saved searches `postinstall` creates |
   | `TRAN_FIND` | View | Read transactions generally — the id and application-target checks (audit N15) read transaction types the role has no Edit permission on, such as a refund's target customer deposit |
   | `LIST_FILECABINET` | Full | Create and attach files in the `files` action |
   | `TRAN_CUSTINVC` | Edit | Invoices |
   | `TRAN_CUSTCRED` | Edit | Credit memos |
   | `TRAN_CUSTPYMT` | Edit | Customer payments |
   | `TRAN_CUSTRFND` | Edit | Customer refunds |
   | `TRAN_SALESORD` | Edit | Sales orders |
   | `TRAN_ESTIMATE` | Edit | Estimates |
   | `TRAN_CASHSALE` | Edit | Cash sales |
   | `TRAN_RTNAUTH` | Edit | Return authorizations |
   | `LIST_CUSTJOB` | Edit | Customers |
   | `LIST_CONTACT` | Edit | Contacts |
   | `LIST_ITEM` | Edit | Items the sync creates or updates |
   | `LIST_ACCOUNT` | View | Read GL accounts |
   | `LIST_SUBSIDIARY` | View | Read subsidiaries |
   | `LIST_CURRENCY` | View | Read currencies |
   | `LIST_TAXITEM` | View | Read tax items |
   | `LIST_TAXSCHEDULE` | View | Read tax schedules |
   | `LIST_PAYMETH` | View | Read payment methods |

   The eight transaction Edit permissions cover every native type an Order node or a custom node can target: sales order, estimate, invoice, credit memo, cash sale, return authorization (plus customer payment and customer refund for the `payments`/`refunds` actions). The role carries no vendor, employee, journal entry, or company-setup permission — nothing that can change scripts, roles, users, or tokens (`tools/ns-access-test.js` asserts this directly). Two cases need an **Administrator** token instead of the integration role:
   - A connection whose custom mappings write to vendors, employees, journal entries, or other record types the role doesn't cover.
   - The one-time `taxsetup` action, which creates Tax Agency vendors and nexuses — permissions this role deliberately does not carry.

   See [Moving a connection to the new role](#move-role) below if you're upgrading a connection that's on Administrator or another role today.
3. **Setup → Users/Roles → Access Tokens → New** — pick the integration, the integration user, and the role; copy the **Token ID / Token Secret** (shown once).

Hand the four values plus the account id to whoever runs the [Salesforce Connection wizard](https://taggalongtech.github.io/continuous-control/connection-wizard.html) — they are entered once in step 1 and never displayed again.

> If the connection fails with a signature error or a 401, confirm the token and the consumer key/secret came from the same integration record, user, and role, and that Token-Based Authentication and REST Web Services are both enabled (see Before you install).

## Moving a connection to the new role {#move-role}

A connection running on Administrator, or on a role from before ns-v3.2.0, keeps working — every RESTlet deployment stays open to all roles in this release (the audience narrowing to Administrator plus the integration role is deferred to a later release). Move it when you're ready:

1. Assign the **Continuous Control Integration** role to the integration user, as in step 2 above.
2. **Setup → Users/Roles → Access Tokens → New** — create a token for that user and role.
3. In the Salesforce [Connection wizard](https://taggalongtech.github.io/continuous-control/connection-wizard.html), open step 1 and enter the new **Token ID** and **Token Secret**. The consumer key and consumer secret stay the same, since they belong to the integration record, not the token.

`ping` and `installcheck` report the connection's role as `role: {id, scriptId, name}`. Wizard step 4 reads it and shows a note when the role is anything other than Administrator or `customrole_cc_integration`: *"This connection uses the \<name\> role. Move it to the Continuous Control Integration role. A later release limits the endpoint to that role and Administrator."* The note is informational only in ns-v3.2.0 — it never fails Verify installation.

## The sync endpoint

The deploy creates the endpoint Salesforce calls: the **Continuous Control (RESTlet)** script record, `customscript_cc_control_rl` with deployment `customdeploy_cc_control_rl`. Those are the ids a new connection uses: leave the two RESTlet fields blank in the [Connection wizard](https://taggalongtech.github.io/continuous-control/connection-wizard.html) and it takes them. Fill them in only to name a renamed deployment; a connection that already stores ids is never changed by saving. Because the record is created by the install, NetSuite registers all three entry points (POST, PUT, GET) on it automatically — there is no script record to re-save by hand.

## Finish from Salesforce

In the Salesforce **Continuous Control** app → **NetSuite Connection** tab: steps 1–3 enter the credentials from above, add the two Remote Site Settings the page lists, and test the connection. Step 4 — **Verify installation** checks every record type, script, and saved search plus the installed package version; **Run post-install setup** creates the **Continuous Revenue Settings** record (`customrecord_cc_settings`, two Disable switches, both unchecked to start) and re-creates any `customsearch_cc_*` saved search missing from the connection. Every one, including the POB sublist search, ships as an SDF object, so this step is normally a no-op. Both actions are safe to re-run, and post-install must be re-run after an upgrade. Steps 5 and later handle environment discovery, field mappings, and reference data — see [The Connection wizard](https://taggalongtech.github.io/continuous-control/connection-wizard.html).

## Remaining NetSuite UI steps

Three things NetSuite offers no deployment API for — full detail and tables in [NetSuite manual setup](https://taggalongtech.github.io/continuous-control/netsuite-manual-setup.html):

1. **Continuous Revenue Settings record** — post-install creates it (`customrecord_cc_settings`, one row). Choose its account values in the Connection wizard's step 4 (**NetSuite account settings**, ns-v3.1.0+): Subsidiary, Currency, Deferred Revenue Account, Revenue Account, Tax Schedule, Tax Item, Refund Bank Account, and Refund Payment Method. Step 4 marks any still on a shipped default and saves all eight in one write. Item Subsidiaries (a fallback list), the two Disable switches, the Default Rule, and Auto-Recognize are set on the record in NetSuite. The refund script parameters on the Continuous Control (RESTlet) deployment (`custscript_cc_restlet_refund_acct` · `custscript_cc_restlet_refund_pm`) are read only when the record's refund fields are blank.
2. **Custom Sublists** — attach five saved-search sublists (the POB list and others on the RC Contract and RC POB). The searches ship as SDF objects, pre-configured; attaching them is a few minutes of clicking. The **GL Impact** tab on transactions needs no attaching: a User Event script fills it on install.
3. **Preferred forms on the RC records** — RC POBs and the other nine RC record types each need a preferred custom form, so they open on their own subtabs instead of NetSuite's Translation/Workflow tabs.

## Upgrades and re-runs

SDF upserts components by their script ids and never deletes, so a plain re-run of `./install.sh` against a newer release zip — validate, confirm, deploy — is always safe on an account already running 3.x.
