Nexus
Documentation / Connectors

NetSuite (REST / SuiteQL)

Connect Nexus to NetSuite over the REST platform — SuiteQL for analytical reads with full server-side pushdown, plus the Record API for per-record create, update, delete, and merge. Understand the dual-surface model, the exact pushdown boundary, and where writes are gated.

The NetSuite REST adapter (bundle version 2.0.8) gives Nexus bidirectional access to your NetSuite ERP over the NetSuite REST platform. It exposes two surfaces on one connection: SuiteQL for SQL-style analytical reads, and the Record API for targeted record writes. This page is for platform and finance-systems engineers configuring a NetSuite bridge; it covers authentication, the exact collection/surface routing model, the precise pushdown boundary between the two surfaces, write and writability rules, and the behavioral limits your integration must plan around.

The most important thing to understand before you build on this adapter is that it is dual-surface with asymmetric pushdown: the SuiteQL surface pushes WHERE, GROUP BY, ORDER BY, aggregates, and pagination down to NetSuite, while the Record API surface pushes nothing — it is a per-record CRUD interface. Choosing the right surface for each operation is the difference between a query that runs server-side in NetSuite and one that pulls records back for in-engine evaluation.

What it connects to

  • SuiteQL (/services/rest/query/v1/suiteql) — a SQL-style query surface. Full WHERE, GROUP BY, ORDER BY, aggregate expressions, and OFFSET/FETCH NEXT pagination are translated and sent to NetSuite, so filtering, grouping, and paging run server-side.
  • Record API (/services/rest/record/v1/<recordType>) — per-record CRUD (GET / POST / PATCH / DELETE) for the supported record types listed below. This is how you write back to NetSuite.

Both surfaces share one set of credentials and one bridge configuration. The query container selects the surface: an empty container or NETSUITE routes to SuiteQL; RECORD routes to the Record API. You never manage two connections — you pick a surface per operation by naming the container.

Authentication

NetSuite authentication is configured per bridge via the auth_method secret. Both methods stored in your tenant's Azure Key Vault; no credential value is ever held inside Nexus itself.

Method When to use Credential mechanics
OAuth 1.0a Token-Based Authentication (TBA) Default for most NetSuite accounts HMAC-SHA256-signed request headers computed per request
OAuth 2.0 client_credentials Accounts with an OAuth 2.0 machine-to-machine integration record enabled Bearer token minted and cached; refreshed roughly 60 seconds before expiry

Required secrets

Secret key Required for Notes
auth_method Both "tba" or "oauth2"
account_id Both e.g. TSTDRV1234567-SB1 (sandbox)
consumer_key Both Integration record consumer key
consumer_secret Both Integration record consumer secret
token_id TBA only Access token ID
token_secret TBA only Access token secret

Secrets are referenced by name from your Key Vault. See Secrets for how credential material is stored and rotated, and Connections & bridges for how a bridge binds a connector to a set of secrets.

TBA realm and host derivation

TBA signing requires the account ID in two different forms, and the adapter handles the transformation automatically:

  • The OAuth 1.0a realm is the account ID uppercased with hyphens converted to underscores (e.g. TSTDRV1234567_SB1).
  • The HTTPS host uses the account ID with hyphens preserved and lowercased (e.g. tstdrv1234567-sb1.suitetalk.api.netsuite.com).

Getting this wrong is the most common cause of a 401 on a first connection; the adapter derives both forms from the single account_id secret so you supply it once.

Capabilities: the dual-surface pushdown boundary

This is the defining characteristic of the adapter. Read it carefully.

SuiteQL surface (NETSUITE) Record API surface (RECORD)
Read Yes — full SQL SELECT Yes — individual record GET by id
Write (insert / update / delete / merge) No Yes
Schema discovery Yes — collections + custom record types
Filter (WHERE) pushdown Yes — pushed to NetSuite No — no query pushdown
Order-by pushdown Yes No
Aggregation / GROUP BY pushdown Yes No
Pagination pushdown Yes — OFFSET/FETCH NEXT No

Because the SuiteQL surface declares full pushdown and its operator set is pushable, the Nexus engine sends your WHERE, ordering, grouping, and limit straight to NetSuite and does not re-filter the returned rows. On the Record API surface there is no query surface at all — it is a per-record interface — so any list-style filtering you express against RECORD is evaluated in the Nexus engine after records are fetched, bounded by the engine's post-process row budget. For anything analytical, always use the SuiteQL surface.

For the general rule Nexus applies here — the engine hands WHERE to an adapter as a hint and re-applies it in memory unless the adapter declares full pushdown and every operator is pushable — see Pushdown & capability semantics.

Supported filter operators (SuiteQL surface)

$eq, $ne, $gt, $gte, $lt, $lte, $like, $in, $not_in, $between, $empty, $contains, $starts_with, $ends_with, $expr

The $expr operator injects a raw SQL fragment into the WHERE clause and is a trusted-author surface — treat report and pipeline authorship as an administrative action.

Writable record types (Record API surface)

The following record types support create, update, delete, and merge (upsert) via the Record API. Merge resolves on the record's NetSuite internal id.

Accounting: account, subsidiary, department, classification, location, budget, budgetCategory

Contacts & relationships: customer, customerCategory, customerStatus, vendor, vendorCategory, employee, contact, contactRole, partner

Items & pricing: item, inventoryItem, pricingGroup, priceLevel

Payments & terms: paymentMethod, term

Projects: job, jobType, jobStatus, projectTask

Two record types are available only through the Record API and are not queryable via SuiteQL: vendorprepayment, vendorprepaymentapplication.

Read-only collections (write-gated)

Several record types are exposed via SuiteQL for reading but are not writable through the Record API — writes to them are rejected before any request is sent. These include transactional and system-derived types such as transaction, transactionLine, currency, and entity. Attempting ctx.adapters.netsuite.write(...) against one of these returns a permission-style error rather than silently no-op'ing. This writability gate is a per-adapter control, layered underneath the tenant RBAC that governs whether the caller may write at all.

Example: read with SuiteQL, then write with the Record API

A pipeline that reads open sales orders analytically (server-side pushdown) and creates a customer record:

async def run(ctx):
    # READ — SuiteQL surface (NETSUITE container). WHERE/ORDER/LIMIT push to NetSuite.
    result = await ctx.adapters.netsuite.read({
        "containers": ["NETSUITE"],
        "collection": "transaction",
        "where": {"type": {"$eq": "SalesOrd"}, "status": {"$ne": "closed"}},
        "select": ["id", "trandate", "entity", "foreignamount"],
        "order_by": [{"field": "trandate", "direction": "desc"}],
        "limit": 500,
    })
    ctx.logger.info("open orders: %d", len(result.rows))

    # WRITE — Record API surface (RECORD container).
    await ctx.adapters.netsuite.write({
        "containers": ["RECORD"],
        "collection": "customer",
        "operation": "insert",
        "records": [{"companyName": "Acme Corp", "email": "ar@example.com"}],
    })

To upsert on internal id, use "operation": "merge" (an alias of upsert) with the record's id present; matched ids update, unmatched ids insert. No credential values appear in the pipeline — the adapter resolves them from Key Vault at call time.

Schema discovery

Discovery enumerates accessible collections and custom record types (customrecordtype). Per-field column lists are returned empty at this time — collection names are enumerated, but per-field metadata is not fetched automatically. To explore fields, run a SuiteQL SELECT * against a small result set, or consult the NetSuite Records Browser. Discovery works against the SuiteQL surface; the Record API surface does not expose a discovery operation.

Limits & behavior notes

  • SuiteQL has no prepared statements. Filter values are inlined into the query string before it is sent to NetSuite. This is why query authoring is a trusted, administrative action — combine it with execution roles and RBAC to bound who can author.
  • Prefer: transient header. All SuiteQL requests carry this header, telling NetSuite not to persist query plans across requests. Pagination uses SuiteQL OFFSET/FETCH NEXT.
  • Record API filtering is in-engine. The RECORD surface fetches records and any filtering happens in the Nexus engine, bounded by the post-process row budget. Do not use it for large scans — route those through SuiteQL.
  • Retry on transient failures. The adapter retries transient HTTP failures up to three times with exponential backoff (2 s initial). This is adapter-level HTTP retry only; it is not pipeline-run retry (Nexus does not automatically retry a failed run — see Reliability & operations).
  • Request timeout. Individual requests time out after 120 seconds; a long pipeline is additionally bounded by the per-run wall-clock timeout.
  • Governance limits are NetSuite's. SuiteQL concurrency and per-account request governance are enforced by NetSuite, not Nexus; heavy analytical extraction may be better served by the Connect surface below.

When to use the Connect surface instead

For heavy analytical SQL — window functions, CTEs, lateral joins, HAVING, set operations, and Oracle SQL extensions — use the NetSuite (SuiteAnalytics Connect) adapter. It is read-only but offers a much richer pushdown surface over JDBC. Both adapters use the same TBA credentials and can be active on the same account, so you route analytical reads to Connect and record writes to REST without duplicating credential setup.

See also