Skip to main content

Oracle NetSuite

The NetSuite connector reads invoices and credit memos out of NetSuite over Oracle's SuiteTalk REST Web Services and SuiteQL, turns each one into an e-invoice and sends it. Nothing is installed in NetSuite and NetSuite never calls GoRoute: GoRoute connects to your account's REST address with an integration your administrator creates. Optionally GoRoute writes the status and the reason for anything it could not send back onto the NetSuite document, and creates the supplier invoices you receive as vendor bills.

Status: built, not yet proven on an account

The connector is built against Oracle NetSuite's published REST Web Services and tested against answers constructed from that documentation. It has not yet exchanged a document with a NetSuite account; the first customer sandbox is the acceptance, and the points under What the first account confirms are open until then. GoRoute switches it on organisation by organisation, for a pilot; without that, connecting answers 403 with FEATURE_NOT_ENABLED. The client's administrator enables REST Web Services and creates the integration record and the two custom body fields. Connect a sandbox first.

Until GoRoute has switched it on for your organisation, there is no NetSuite panel under Connectors.

What your NetSuite administrator sets up​

  1. Enable the features. REST Web Services, and the login you will use: OAuth 2.0 (recommended) or token-based authentication. Oracle's help: REST Web Services URL Schema and Account-Specific URLs.
  2. A role for the integration that can use REST Web Services and SuiteQL, and can read invoices, credit memos, customers, tax codes and currencies. With write-back on it also edits invoices and credit memos; with vendor bills on it creates vendor bills and vendor credits and reads vendors. The first account confirms the exact permission names.
  3. An integration record and the login itself:
    • OAuth 2.0 client credentials (machine to machine, no user signs in): the integration's client ID, and a certificate mapped to the integration and the role in OAuth 2.0 Client Credentials (M2M) Setup. GoRoute signs with the certificate's private key (PS256, PS384, PS512, ES256, ES384 or ES512; NetSuite does not accept RS256). Oracle's help: OAuth 2.0 Client Credentials Flow, The Request Token Structure.
    • Token-based authentication: the integration's consumer key and secret, and an access token's ID and secret for the integration user and role. Oracle's help: The Authorization Headers. Oracle's documentation says that from NetSuite 2027.1 no new token-based integration can be created for REST web services, so choose OAuth 2.0 for a new integration.
  4. Two custom body fields on Sale > Invoice and Credit Memo, if you want the status and the reason written back (Customization > Lists, Records & Fields > Transaction Body Fields): a Free-Form Text field for the status and a Text Area for the reason. The default script IDs are custbody_goroute_status and custbody_goroute_reason; others can be named on the connector.
  5. For vendor bills (optional): the internal ID of the expense account that received supplier invoices are booked to until your accountant codes them, and a VAT number on each vendor.

Connect​

On Connectors β†’ NetSuite, enter the account ID (Setup > Company > Company Information; 1234567, or 1234567_SB1 for a sandbox), the subsidiary's internal ID if you run OneWorld and this GoRoute organisation is one subsidiary, and the login. Connect and test proves the login and runs the very query the connector uses to find new documents. Tick Send new invoices and credit memos automatically to start: GoRoute starts at the date you give, or at the moment you turn it on, and never sends older documents.

The same through the API (OAuth 2.0 shown; for token-based authentication send consumer_key, consumer_secret, token_id and token_secret instead):

curl -X POST https://app.goroute.ai/peppol-api/api/v1/connectors/{id}/connect \
-H "X-API-Key: your_api_key" -H "Content-Type: application/json" \
-d '{
"config": {
"account_id": "1234567_SB1",
"subsidiary_id": "3",
"auto_sync": true,
"sync_from": "2026-11-07",
"writeback": true
},
"credentials": {
"client_id": "…",
"certificate_id": "…",
"private_key": "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n",
"algorithm": "PS256"
}
}'

The credentials are stored encrypted and never returned or logged. Connect again with no credentials to change settings on a connection that already has them; GoRoute re-tests with the stored ones. The settings:

SettingMeaning
account_idThe NetSuite account ID. The REST address is <account>.suitetalk.api.netsuite.com.
rest_domainOnly if Company Information > Company URLs shows a different SuiteTalk REST domain.
subsidiary_idOneWorld: send only this subsidiary's documents. One subsidiary is one GoRoute organisation.
auto_sync, sync_fromSend automatically, from this date (UTC). Off by default.
writeback, status_field, reason_fieldWrite the status and the reason back. Off by default.
party_map{NetSuite customer internal ID or entity ID: "scheme:identifier"} for customers you address yourself.
customer_peppol_fieldThe script ID of a custom entity field (custentity…) that holds a customer's Peppol ID.
tax_code_map{tax code internal ID or name: {"category": …, "exemption_code": …}} β€” see Tax.
ap_intake, ap_expense_account_idCreate received supplier invoices as vendor bills. Off by default.

The connection may be OAuth 2.0 or token-based; which one is decided by the credentials you send, and sending both is refused.

What is sent​

GoRoute sends invoices and credit memos that are posted. An invoice waiting for approval is not sent; its approval changes the record and GoRoute picks it up then. A voided invoice that was never sent needs nothing. One voided after it was sent is held: a sent e-invoice cannot be withdrawn, so issue a credit memo in NetSuite.

The customer. In this order: the connector's party map; the customer's own Peppol ID field; for a seller in Oman, the customer's Omani VAT number under scheme 0248; then the Peppol network, from the customer's VAT number (the invoice's own, else the customer's tax registration). A customer GoRoute cannot find is held, never sent to a default.

The seller. The connector's seller settings, with anything missing taken from your company settings in GoRoute.

Lines. Each item line with its quantity, rate and amount; a discount item becomes a negative line; subtotal and description lines are not lines of their own; shipping becomes a line. A discount on the whole invoice (the header discount item) is not sent yet: put it on a line, or send that invoice from the portal.

Tax​

A line with a positive rate is standard-rated. A 0 % tax code is held until you say what it is in the connector's tax_code_map: NetSuite's code does not say whether it is zero-rated, exempt, reverse charge, intra-Community, export or outside the scope of VAT, and each is written differently on an e-invoice. For example:

{"tax_code_map": {"8": {"category": "intra_community", "exemption_code": "VATEX-EU-IC"}}}

Categories: standard, reduced, zero, exempt, reverse_charge, intra_community, export, outside_scope. Once mapped, every invoice that uses the code is sent. For a seller in Oman the country's reason code is added from your settings where the map gives none.

Status and reason in NetSuite​

With write-back on, GoRoute writes Sent into the status field when a document goes out, and Held with the reason when it is held β€” once per reason, so a reason that has not changed is not written again. If NetSuite refuses the write (the field is missing, or the role cannot edit the record), the document's outcome stands, the panel counts the refusal, and GoRoute asks again after fifteen minutes.

Held documents​

A document GoRoute cannot send is held on the connector, with the reason and where to correct it: the invoice, the customer, the tax code map, or your company settings. The NetSuite panel lists them with Retry now, Skip and Never send; so do held_invoices on GET /api/v1/connectors and GET /api/v1/connectors/held-invoices, as for every pull connector (see When an invoice cannot be sent).

  • A document corrected in NetSuite is picked up by its change on the next check.
  • A fix elsewhere (the customer, the map, your company settings) is picked up within about 15 minutes, or at once with Retry now or Sync now.
  • A fault on GoRoute's side is retried on every check.

GoRoute never sends the same document twice: each send is keyed on the account, the record type and the document's internal ID, and a document already sent is recognised by its number and skipped.

If NetSuite cannot be reached, the panel says so and GoRoute waits longer between attempts, up to ten minutes. Connect or Test connection clears the wait.

Supplier invoices as vendor bills​

With vendor bills on, each supplier invoice GoRoute receives for you over Peppol that is not yet delivered elsewhere becomes a vendor bill (a credit note, a vendor credit): the vendor whose VAT number is the supplier's, the supplier's number and dates, and one expense line per supplier line on the account you named, without a tax code. GoRoute never creates a vendor; with none or several matching, the document waits in GoRoute with that reason. NetSuite's own vendor-bill approval decides what happens next: if approval routing is off in your account, NetSuite posts the bill when it is created, so turn approval on if your accountant should approve each one.

Sync now​

POST /api/v1/connectors/{id}/sync runs one check at once and returns what it did. With automatic sending off it pulls nothing and says so.

How changes are found​

GoRoute asks SuiteQL for invoices and credit memos whose lastmodifieddate is at or after its last mark, oldest change first, a page at a time (Executing SuiteQL Queries Through REST Web Services, Collection Paging), and skips each document it has already handled at that same change time. It looks back an hour and a quarter behind its mark: fifteen minutes for a document saved by a long-running transaction after a later one, and an hour because SuiteQL's time may be your local time, which repeats an hour when daylight saving ends. The start date is checked against the record's lastModifiedDate, which the REST record service gives in UTC (Using Datetime Fields).

GoRoute makes one request at a time to your account, so it takes one of the account's concurrent requests and no more, and waits and asks again when NetSuite answers that the account is over its limit (Web Services and RESTlet Concurrency Governance).

What the first account confirms​

Oracle's documentation leaves these open; the connector says so in its code and its tests stand in for each with a double:

  • How a sandbox account ID becomes its REST host and its token-based-authentication realm (the connector assumes 1234567-sb1 and 1234567_SB1; rest_domain overrides the host).
  • That SuiteQL accepts TO_CHAR with a date-time mask on lastmodifieddate, and in which time zone it renders it. Connect and test runs that exact query, so a refusal shows at once.
  • The SuiteQL column names (transaction.type values CustInvc and CustCred, voided, posting, the mainline subsidiary, currency.symbol, vendor.vatregnumber).
  • The record fields read from invoices, credit memos, customers and tax codes (legacy tax and SuiteTax), and the write shape of a custom body field.
  • Creating a vendor bill with an external ID, and reading it back by that ID.
  • Whether a request over the concurrency limit carries a Retry-After header.
  • The role's exact permission names.