---
title: "NetSuite"
description: "Stream NetSuite records into Streamkap using OAuth 2.0 with a certificate, or an existing token-based integration"
---

The NetSuite source pulls records — customers, vendors, items, transactions, and more — out of your NetSuite account and streams them into Kafka topics, one topic per record type. Records land flat in your destination like any other Streamkap source, so every existing destination consumes them unchanged.

:::info
NetSuite is an **API source**, not a CDC source. It polls the NetSuite REST API on a schedule rather than reading a database log. See [API sources](/api-sources) for what that means for pipelines, snapshots and delivery.
:::

## How It Works

Streamkap queries your account with **SuiteQL**, NetSuite's SQL interface over SuiteTalk REST. Each selected record type becomes one query and one topic, and each poll asks only for rows changed since the previous poll.

Streamkap uses SuiteQL rather than the REST record service because the record service returns a list of links — one extra API call per row — and cannot filter or sort on the server. SuiteQL returns whole rows in one call and can ask NetSuite for only what changed, which is what makes incremental streaming possible.

## Authentication options

NetSuite offers two, and the form opens on the first.

### OAuth 2.0 with a certificate (recommended)

Streamkap signs each token request with a private key whose public certificate you upload to NetSuite. There is no shared secret to leak or rotate on a schedule, and it is the method Oracle recommends for server-to-server integrations.

### Token-based authentication (TBA)

The older method, based on OAuth 1.0a. Choose it **only if you already have a working TBA integration** you want to point at Streamkap.

:::warning
Oracle stops allowing **new** token-based integrations for REST web services in **NetSuite 2027.1**. Existing ones keep working, but new setups should use the certificate method above.
:::

## Prerequisites

* A NetSuite account, and a role with administrator access to create integration records.
* The **SuiteCloud** features enabled — see below.
* OpenSSL, or any tool that can generate a key pair, if you are using the certificate method.

## NetSuite Setup

### 1. Enable the required features

Go to **Setup → Company → Enable Features → SuiteCloud** and enable:

* **REST Web Services**
* **OAuth 2.0** (for the certificate method) or **Token-Based Authentication** (for TBA)

### 2. Find your Account ID

Go to **Setup → Company → Company Information**. Your **Account ID** looks like `TSTDRV1234567` or `1234567`; a sandbox adds a suffix, `1234567_SB1`.

:::info
Enter the Account ID exactly as NetSuite shows it, underscore included. Streamkap converts it to the API hostname for you — sandboxes use a hyphen there (`1234567-sb1`), and getting that wrong is a common cause of connection failures when people build this by hand.
:::

### 3. Generate a key pair (certificate method)

Run this locally. The private key never leaves your machine except to be pasted into Streamkap, where it is encrypted at rest.

```bash
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 730 -nodes \
  -keyout netsuite-private.pem -out netsuite-public.pem -subj "/CN=streamkap"
```

An EC P-256 key works too if you prefer one; Streamkap detects which you used and signs accordingly.

### 4. Create the integration record

Go to **Setup → Integration → Manage Integrations → New**.

* Give it a name, for example `Streamkap`.
* Under **OAuth 2.0**, tick **Client Credentials (machine to machine) Grant** and the **REST Web Services** scope.
* Untick **Token-Based Authentication** and **Authorization Code Grant** — Streamkap does not use either in this mode.
* Save, then copy the **Client ID** (also called Consumer Key). NetSuite shows it once.

For TBA instead: tick **Token-Based Authentication**, save, and copy the **Consumer Key** and **Consumer Secret**.

### 5. Map the certificate (certificate method)

Go to **Setup → Integration → OAuth 2.0 Client Credentials (M2M) Setup → Create New**.

* Pick the integration record from step 4, the role you prepared in step 6, and the entity that role belongs to.
* Upload `netsuite-public.pem`.
* Save, then copy the **Certificate ID**.

:::warning
The **Certificate ID** is its own value — it is not the Client ID, and not the certificate's fingerprint. A wrong one is rejected on every request with no further explanation, so copy it from this screen rather than reconstructing it.
:::

### 6. Grant the role its permissions

The role you mapped above needs:

* **Setup → REST Web Services**
* **Setup → SuiteAnalytics Workbook**
* **Setup → Log in using OAuth 2.0 Access Tokens** (or **Access Tokens** for TBA)
* **View** access to every record type you plan to stream

Streamkap checks each record type you select before saving the source, so a missing permission is reported against that record type rather than failing quietly later.

### 7. Create an access token (TBA only)

Go to **Setup → Users/Roles → Access Tokens → New**, pick the integration and role, and copy the **Token ID** and **Token Secret**.

## Streamkap Setup

Follow these steps to configure your new connector:

### 1. Create the Source

* Navigate to [Add Connectors](https://app.streamkap.com/connectors/add?tab=Sources).
* Choose **NetSuite**.

### 2. Connection Settings (Auth tab)

* **Name**: A name for your connector (for example `netsuite-prod`).
* **Account ID**: From [step 2](#2-find-your-account-id), exactly as NetSuite shows it.
* **Authentication**: **OAuth 2.0 with a certificate** (the default) or **Token-based authentication**. The rest of the form follows your choice.

**OAuth 2.0 with a certificate**

* **Client ID**: The Client ID from your integration record.
* **Certificate ID**: From the M2M setup screen.
* **Private Key**: The contents of `netsuite-private.pem`, including the `BEGIN` and `END` lines. Stored encrypted at rest and never shown again.
* **Private Key Passphrase**: Only if you protected the key with one.

**Token-based authentication**

* **Consumer Key** and **Consumer Secret** from the integration record.
* **Token ID** and **Token Secret** from the access token.

### 3. Settings tab

* **Records**: The record types to stream. Each becomes its own topic.
* **Backfill start date** (optional): How far back the first sync reaches. Leave it empty to load everything.

### 4. Review and create

Check the summary and click **Create**. Streamkap verifies your credentials and confirms it can read each record type you selected, then the source moves to **Active**.

## Editing the Source

### Rotating the certificate

Upload a new certificate in NetSuite, then edit the source and paste the new private key and Certificate ID. Streaming continues from where it left off.

### Adding records

Edit the source and add record types to the **Records** field. New topics appear for the new records; existing ones keep their position and are not re-read.

## Sending Topics to a Destination

API sources do not use pipelines. Use **Send to destination** from the Topics page, the source's Destinations tab, or a destination's Add topics dialog. See [API sources](/api-sources#where-to-find-send-to-destination).

## Supported Records

Streamkap offers these record types:

| Entities | Items and transactions | Accounting and dimensions |
|---|---|---|
| Customer | Item | Account |
| Vendor | Transaction | Subsidiary |
| Contact | | Department |
| Employee | | Location |
| Partner | | Classification |
| | | Currency |
| | | Accounting Period |

**Transaction** covers every transaction type — invoices, sales orders, purchase orders and the rest — in one topic, with a `type` column identifying each row. NetSuite models them as one table, so Streamkap follows suit.

### Custom records

Custom records are supported. Enter the record's SuiteQL table name, which starts with `customrecord`, for example `customrecord_project_phase`. Streamkap verifies it before saving.

## Behavior & Limits

### Incremental sync

Each poll asks NetSuite for rows whose last-modified timestamp is at or after the previous poll's high-water mark. The boundary is re-read each time and duplicates removed, so a row modified in the same second as the cutoff is never skipped.

### An extra column

Every row carries an additional `streamkap_lastmodified` column alongside your record's own fields. NetSuite returns dates in your account's display format by default, which cannot be compared reliably, so Streamkap asks for a second copy of the timestamp in a fixed format and uses that to track progress. It is safe to ignore, and safe to use.

Every value arrives as text. NetSuite's query service returns numbers as strings — a record's `id` is `"101"`, not `101` — and booleans as `"T"` or `"F"` rather than true/false. Streamkap passes rows through as NetSuite sends them, so that is what your destination receives: columns created from these rows are string-typed, and downstream models should cast them rather than assume a numeric or boolean type.

### Deletes

Streamkap periodically reads NetSuite's deleted-records log and writes a **delete marker** for each deleted row — a record carrying `__deleted = true`, not an empty Kafka tombstone — and your destination removes the row when it applies it. Every deletion is confirmed against the live record first, so a record that was deleted and recreated under the same internal ID is never removed by mistake.

Delete markers cover the standard record types. **Custom records do not get them**: NetSuite's deleted-records log identifies a custom record by its script ID rather than its table name, so those deletions cannot be matched reliably and are left out rather than guessed at.

### Rate limits

NetSuite limits how many API requests an account runs at once, based on your service tier and any SuiteCloud Plus licences. Streamkap uses a single connection per source and paces its requests, so it does not compete with your other integrations.

### Record volume

A single poll reads up to 10,000 records, then continues from that point on the next poll. Large initial loads therefore arrive over several polls rather than in one long run.

## Troubleshooting

**"NetSuite rejected the integration's client assertion"** — the Client ID, Certificate ID, or private key does not match what NetSuite has. Check the Certificate ID on the M2M setup screen, and that the certificate has not expired or been rotated.

**"NetSuite private key could not be read"** — the key was not pasted whole. Include the `BEGIN` and `END` lines, and supply the passphrase if the key has one.

**A record type is rejected when you save** — the message names it. Either the role lacks View access, or that record type has no last-modified timestamp and cannot be streamed incrementally. Grant the permission, or remove that record from the selection.

**Nothing arrives after the source goes Active** — confirm the records you selected have rows modified since your backfill start date. A source with no recent changes is idle, not broken.

## See Also

**[API sources](/api-sources)**

How API sources differ from CDC sources, and how to deliver their topics.

**[Send to destination](/api-sources#where-to-find-send-to-destination)**

Getting NetSuite topics into your warehouse.
