Skip to content

A Practical NetSuite Migration Guide: Moving Off SOAP Before 2028

Oracle is deprecating NetSuite SOAP APIs by 2028. Learn to architect a modern migration with SuiteQL, REST, and RESTlets - plus how Truto and Prismatic compare for the job.

Nachi Raman Nachi Raman · · 36 min read
A Practical NetSuite Migration Guide: Moving Off SOAP Before 2028

Oracle NetSuite is aggressively phasing out its legacy SOAP web services. If your B2B SaaS platform relies on SuiteTalk SOAP endpoints to sync accounting, inventory, or HRIS data—or if you are connecting AI agents to ERP data—you are currently operating on borrowed time.

Oracle has set a hard deadline: by the 2028.2 NetSuite release, all SOAP endpoints will be permanently disabled, and SOAP-based integrations will stop working. If you maintain a NetSuite integration at a B2B SaaS company, this is not a "nice to have" migration. It is a mandated one with a fixed countdown. Engineering teams must immediately plan to create a practical NetSuite migration guide without SOAP to avoid breaking critical customer workflows.

Migrating off NetSuite SOAP is rarely a simple endpoint swap. Modern REST APIs offer predictable semantics, standard JSON payloads, and logical routing. NetSuite's API ecosystem offers none of these out of the box. Attempting to map legacy SOAP XML requests directly to NetSuite's SuiteTalk REST API will immediately expose your infrastructure to severe performance bottlenecks, missing metadata, and aggressive concurrency throttling.

This guide provides the architectural playbook you need to survive this transition. We will examine the exact deprecation timeline, why a naive move to REST is an engineering trap, and how to architect a modern, reliable NetSuite integration using a hybrid approach of SuiteQL, REST, and SuiteScript. The comparison of NetSuite API web services versus REST is not one-to-one, so we will also walk through the phased migration playbook - inventory, read migration, write migration, RESTlet deployment, testing, rollout, and rollback - with templates you can copy directly into your team's working docs.

The NetSuite SOAP API Deprecation Timeline (2025–2028)

Oracle has been methodical about this retirement. The deprecation is a phased rollout, not a sudden cliff. Oracle's messaging to developers is unambiguous: any new capability introduced in NetSuite will only be available through modern REST surfaces. The legacy XML endpoints are entering a strict maintenance phase.

Here is the strict schedule you must architect around:

Release What Happens
2025.2 Last planned SOAP endpoint ships. No subsequent SOAP endpoints will be released unless required for critical business continuity.
2026.1 No new features will be exposed via SOAP. Any new capabilities introduced in NetSuite will only be available through REST.
2027.2 SOAP usage is restricted exclusively to the single 2025.2 endpoint. All older endpoints are permanently retired.
2028.2 Complete removal. All SOAP endpoints are disabled. All SOAP integrations break.

Oracle's own documentation is blunt about the reasoning: SOAP does not support the latest business features, new records have not been made available to SOAP for years, and the underlying XML technology stack does not support modern architecture standards like SuiteAnalytics Workbooks and SuiteScript 2.x Analytics APIs.

For engineering teams, this timeline creates an immediate architectural mandate. You cannot wait until 2027 to begin this migration. Enterprise ERP integrations are notoriously difficult to refactor. Gartner research indicates that 75% of ERP implementation projects run into serious setbacks, often due to integration architecture and data mapping failures. Rebuilding a NetSuite integration requires auditing hundreds of custom fields, validating multi-currency routing logic, and rewriting complex data transformations.

If you are still on SOAP right now, you are already accumulating technical debt with every NetSuite release. You must transition your infrastructure to communicate directly with NetSuite's modern API surfaces before the hard cutoff.

Mapping Your Migration Phases to NetSuite Release Dates

Before committing to a project plan, translate Oracle's release calendar into your internal delivery milestones. This table is the executive planning view - use it to set quarterly OKRs and secure engineering headcount.

Your Phase Suggested Target NetSuite Release Constraint
Phase 0: Inventory and impact assessment Q1 2026 Complete before 2026.1 freezes new SOAP features
Phase 1: Read migration to SuiteQL Q2-Q3 2026 Reads are 70-90% of your call volume
Phase 2: Write migration to REST Q4 2026 - Q1 2027 Take advantage of 2026.1 homogeneous batch REST
Phase 3: RESTlet deployment for gaps Q2 2027 Complete before 2027.2 SOAP restrictions bite
Parallel run and reconciliation Q3 2027 60-90 days minimum in shadow mode
Cutover and SOAP decommission Q4 2027 - Q1 2028 Leave a 6+ month buffer before 2028.2

A team of 2-3 engineers migrating a moderately complex NetSuite integration (10-20 resources, 50+ custom fields) should budget 4-6 months of focused work. Add 30-50% if you also support QuickBooks, Xero, or Sage Intacct behind the same product surface.

Why "Just Use the REST API" is a Migration Trap

When developers realize SOAP is dying, their first instinct is to rewrite every request to target the suitetalk.api.netsuite.com/services/rest/record/v1/ endpoints. The most common mistake teams make is treating this as a find-and-replace from SOAP to REST.

This approach will fail in production. The SuiteTalk REST API is fundamentally designed for single-record CRUD operations. It is actively hostile to bulk data extraction and complex relational queries. Attempting a 1-to-1 migration will expose you to three severe limitations:

1. The N+1 Sub-Resource Problem

The most severe limitation is how the REST Record Service handles sub-resources. When retrieving a list of records, the REST API does not allow developers to use the expandSubResources parameter.

If you query a list of 200 Purchase Orders and need the line-item details for each, the REST API forces you to make 200 additional, separate API calls. SOAP's legacy getList operation returned everything in one shot. REST forces you into a massive N+1 query bottleneck.

2. No Ordering and Hard Pagination Ceilings

The REST Record API lacks basic querying flexibility. You cannot specify the order in which records are returned—there is no equivalent of an ORDER BY clause. Furthermore, you can get a maximum of 1,000 records per page, and you can only retrieve the first 1,000 pages of results. For enterprise NetSuite accounts that have a massive volume of historical transaction records, this hard ceiling makes complete data synchronization impossible.

3. Single-Record Writes (Until Recently)

Historically, NetSuite's REST Record API processed exactly one record per request for creates or updates, whereas SOAP handled batch operations of up to 1,000 records. The 2026.1 release finally introduces homogeneous batch operations for REST, allowing developers to submit multiple same-type operations in a single asynchronous REST call. However, if your customers are on older releases or rely on heterogeneous batches, you are stuck with one-at-a-time writes.

The bottom line: a 1-to-1 SOAP-to-REST migration will dramatically increase your API call volume. Given NetSuite's strict concurrency limits, this spike in network requests will instantly trigger a cascade of HTTP 429 errors and block all other integration traffic for that customer. To safely architect a reliable NetSuite API integration, you must abandon the idea of a pure REST implementation.

The Tri-Partite Architecture: SuiteQL, REST, and RESTlets

The real replacement for SOAP isn't just REST. It is a combination of three distinct API surfaces, each handling what it does best. These three surfaces - SuiteQL, SuiteTalk REST, and SuiteScript RESTlets - are the practical NetSuite API SOAP alternatives that together cover every SOAP capability. To bypass the limitations of the REST API, your integration layer must intelligently route requests based on the operation type.

flowchart TD
    A[Unified API Request] --> B{Operation Type}
    B -->|Complex Reads / Lists / JOINs| C[SuiteQL Endpoint]
    B -->|Single Record Write / Update| D[SuiteTalk REST API]
    B -->|PDFs / Dynamic Metadata / Gaps| E[SuiteScript RESTlet]
    
    C --> F[(NetSuite Database)]
    D --> F
    E --> F

1. SuiteQL: Your Primary Read Layer

SuiteQL is NetSuite's proprietary, SQL-like query language. It is exposed via a POST request to /services/rest/query/v1/suiteql. It is the single biggest upgrade over SOAP for data reads, and nearly all list and get operations in your migration should target this endpoint.

Unlike the REST Record API, SuiteQL allows you to execute complex JOINs across related tables in a single network request. A query for vendors can easily JOIN entity addresses, subsidiary relationships, and currency tables without triggering N+1 bottlenecks. It supports advanced WHERE clauses, aggregation (SUM, COUNT, GROUP BY), case-insensitive LIKE searches, standard offset pagination, and computed columns via BUILTIN.DF() for display values.

Here is what a typical SuiteQL vendor list query looks like:

SELECT
  v.id,
  v.companyname,
  v.email,
  ea.addr1,
  ea.city,
  ea.state,
  ea.zip,
  BUILTIN.DF(v.subsidiary) AS subsidiary_name
FROM vendor v
  LEFT JOIN entityaddress ea ON v.defaultbillingaddress = ea.nkey
WHERE v.isinactive = 'F'
  AND v.lastmodifieddate >= '2026-01-01'
ORDER BY v.companyname
FETCH FIRST 100 ROWS ONLY

One API call. Vendors, addresses, subsidiary names. No N+1. The trade-off is that SuiteQL is strictly read-only. It is your data extraction engine, not your mutation engine.

2. SuiteTalk REST: Your Write Layer

Reserve the SuiteTalk REST API exclusively for writes. When your application needs to create a customer, update an invoice, or delete a tracking category, format a standard JSON payload and issue a POST, PATCH, or DELETE request to the specific record endpoint.

Because these are targeted, single-record operations, they are less likely to exhaust concurrent thread limits. For single-record reads where you need expanded sub-resources (like line items on a specific purchase order), the REST API with ?expandSubResources=true works well. Just do not use it for bulk listing.

3. RESTlets (SuiteScript): Filling the Gaps REST Can't

Certain critical capabilities are entirely absent from both SuiteQL and the REST API. Oracle's own documentation acknowledges that developers should not expect 100% parity of REST with SOAP, explicitly stating that when an object or method is not available in REST, developers should use SuiteScript RESTlets instead.

For these edge cases, you must deploy a custom SuiteScript Suitelet into the customer's NetSuite account:

  • PDF Generation: The REST API has no PDF rendering capability. If your integration needs to download a Purchase Order PDF, your Suitelet must utilize the server-side N/render module to generate the binary via render.transaction().
  • Dynamic Form Metadata: NetSuite forms are highly dynamic. Custom fields vary per account, and select options change based on the current record state. The standard REST metadata catalog provides basic schema info, but it cannot tell you which fields are mandatory on a specific custom form at runtime. A deployed Suitelet can create an in-memory record using record.create() and introspect its properties to return accurate field IDs and mandatory flags.
  • Legacy Tax Data: Currently, the SuiteQL salestaxitem table does not expose the full tax rate configuration, including nested tax type references. If your application requires deep tax compliance data, a RESTlet acts as your escape hatch.

Handling NetSuite Authentication and Concurrency Limits

The OAuth 1.0 TBA Math Problem

NetSuite uses OAuth 1.0 Token-Based Authentication (TBA) for server-to-server integrations. This is not modern OAuth 2.0. Every single API request requires an Authorization header containing an OAuth 1.0 signature that must be computed dynamically.

The signature requires combining five credentials (the Consumer Key and Consumer Secret from the integration record, plus the Token ID and Token Secret from the TBA access token, and the Account ID), a randomly generated nonce, a Unix timestamp, the full canonical request URL, and the HTTP method.

These elements must be sorted and encoded according to strict OAuth 1.0 parameter rules to form a base string. You then compute the signature using HMAC-SHA256(consumer_secret&token_secret, base_string). A single misplaced character or incorrect URL encoding will result in an opaque authentication failure.

Building this logic from scratch is a massive engineering tax. This is why shipping API connectors as data-only operations using declarative configuration—where the auth scheme, credential paths, and signature algorithm are defined as data rather than hardcoded logic—is vastly superior to writing custom NetSuite authentication handlers. (Note: NetSuite's newer releases do support OAuth 2.0 for REST, which you should evaluate to simplify your auth layer going forward, but TBA remains ubiquitous).

Surviving Concurrency Limits, Not Rate Limits

Here is a point many teams get wrong: NetSuite does not impose traditional daily API rate limits. It enforces concurrent thread limits based on the customer's service tier.

The default tier allows just 15 concurrent requests. Tier 2 gets 25; Tier 3 gets 35; Tier 4 gets 45; Tier 5 gets 55. Each SuiteCloud Plus license adds 10 additional concurrent threads to this base pool. This means you can make millions of requests per day—as long as no more than your allotted limit are in flight at the exact same time.

When you exceed the allowed concurrent threads, NetSuite immediately responds with an HTTP 429 Too Many Requests error. How your external system reacts determines whether your integration recovers or crashes entirely. A common mistake in custom middleware is to retry a failed request immediately. NetSuite rejects Request A, middleware immediately retries Request A, meanwhile Request B arrives, and NetSuite rejects both, causing a distributed deadlock.

Your integration architecture must treat HTTP 429s as an expected operational state. The correct pattern is exponential backoff with jitter. The platform should normalize upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. When NetSuite returns a 429, you must pass that error directly back to the caller. Do not automatically retry or silently absorb rate limit errors in a proxy layer, as this obscures the real concurrency situation. If you want to know how to normalize pagination and error handling across 50+ APIs, the secret is aggressive standardization at the proxy layer and disciplined retry logic at the client layer.

How to Create a Practical NetSuite Migration Guide Without SOAP

To successfully move your infrastructure off legacy XML endpoints without breaking production, execute the following architectural steps.

1. Audit Existing SOAP Payloads for Complex JOINs

Begin by logging every legacy SOAP request your system currently makes. Categorize each by operation type (read, write, metadata), record types touched, and batch usage. Identify the operations that rely heavily on nested XML structures to pull data across multiple NetSuite tables. These are the danger zones. Do not attempt to map these to the REST Record API. Document the exact tables and fields required, and prepare to rewrite these extractions as SuiteQL queries.

2. Implement Polymorphic Resource Routing

NetSuite's underlying data model is heavily fragmented. From an accounting perspective, vendors and customers are both simply entities you transact with. However, NetSuite treats them as entirely separate record types with separate database tables. Similarly, classes, departments, and locations are all forms of organizational segmentation but exist in isolation.

When migrating off SOAP, do not expose this fragmentation to your internal application logic. Implement a mapping configuration that links unified fields to provider-specific fields. Create a single, polymorphic contacts resource in your application. Use a query parameter or discriminator field (contact_type) to dynamically route the request to either the vendor or customer SuiteQL table or REST endpoint. This isolates your core application from NetSuite's schema quirks and matches how modern accounting platforms expose similar concepts.

3. Build Feature-Adaptive Query Logic

No two NetSuite instances are identical. NetSuite is a platform with wildly different configurations per customer. Some use NetSuite OneWorld (which requires subsidiary mapping), while others use standard editions. Some operate in multi-currency environments, while others are single-currency.

Your SuiteQL queries cannot be static strings; they must be feature-adaptive. A vendor query that JOINs the currency and subsidiary tables will fail fatally on a standard account that doesn't have those features enabled. During the initial connection setup, your integration should query the customer's NetSuite metadata to detect the active features. Based on this context, your query construction logic must dynamically include or exclude specific JOINs.

4. Transition to Declarative Mappings

Maintaining integration-specific code for NetSuite is a massive liability. Every time Oracle updates a schema requirement, you are forced to initiate a code deployment.

Transition your integration layer to use declarative mappings. Define the translation between your application's unified schema and NetSuite's native format using functional expression languages like JSONata. This allows you to handle NetSuite's flat PascalCase fields, custom field detection, and dynamic URL generation entirely as data operations. Separating your mapping logic from your execution pipeline is non-negotiable for long-term stability.

5. Run in Parallel, Then Cut Over

Do not attempt a big-bang migration. Run your new REST/SuiteQL integration in shadow mode alongside the existing SOAP integration:

  1. Route reads through SuiteQL first, and compare the JSON output to your legacy SOAP XML output.
  2. Once reads match flawlessly, route writes through the REST API, verifying the results against SOAP.
  3. Cut over workflow by workflow, hidden behind feature flags.
  4. Decommission SOAP paths only once validated in production.

More than 70% of ERP initiatives fail due to poor change management and insufficient testing. A parallel-run approach drastically de-risks the transition.

Phase 0: Inventory and Impact Assessment

You cannot migrate what you have not measured. Phase 0 produces the source of truth every subsequent phase depends on. Budget 2-4 weeks with one senior engineer.

Build the SOAP Endpoint Inventory

Log every SOAP request your system currently makes over a representative 30-day window. Capture:

  • Operation (get, getList, add, update, delete, search, upsert)
  • Record type (Vendor, Customer, PurchaseOrder, SalesTaxItem, etc.)
  • Call volume per day (average and peak)
  • Payload shape (which fields are read, which are written)
  • Downstream consumers (which internal service or customer workflow depends on this call)
  • Batch size (records per request for bulk operations)
  • Whether nested/expanded data is required (line items, sublists, related records)

Sample inventory row template:

soap_operation record_type daily_avg_calls daily_peak_calls fields_read fields_written batch_size needs_sublists consumer
getList PurchaseOrder 1,200 4,800 45 0 100 yes (item line) invoice_sync_worker
add Vendor 80 250 0 22 1 no vendor_onboarding_flow
get SalesTaxItem 300 900 12 0 1 yes (tax type ref) tax_calculator

Score Each Endpoint by Migration Difficulty

For every row, assign a difficulty score. This score decides prioritization and which phase the endpoint lands in.

  • Green (easy): Direct SuiteQL equivalent exists, no sublists, standard fields only. Ship in Phase 1.
  • Yellow (moderate): Requires JOINs, custom field detection, or edition-specific handling (multi-currency, OneWorld). Also Phase 1 or 2 depending on read vs write.
  • Red (hard): No REST or SuiteQL equivalent - requires a RESTlet (PDF rendering, dynamic form metadata, full tax rate records). Triggers Phase 3.

Prioritize green endpoints first: they build team confidence, prove the shadow-mode pipeline, and knock out the majority of call volume with the least risk.

Map Each SOAP Call to Its Replacement Surface

Produce a two-column decision doc: current SOAP call → target replacement.

Current SOAP Call Replacement Surface Rationale
getList(PurchaseOrder) with lines SuiteQL query with JOIN to transactionline Bulk read with sublist data in one call
get(PurchaseOrder) full detail REST record with ?expandSubResources=true Single-record read with sublists
add(Vendor) POST /services/rest/record/v1/vendor Standard single-record write
update(Invoice) PATCH /services/rest/record/v1/invoice/{id} Partial updates supported
getList(SalesTaxItem) SuiteQL for IDs, then RESTlet or SOAP fallback SuiteQL exposes only partial data
search(Employee) SuiteQL on employee table Full filtering and JOINs
PDF download of PurchaseOrder SuiteScript RESTlet with N/render REST has no PDF capability

Impact Assessment: What Breaks If You Do Nothing

Quantify the blast radius. For each customer account:

  1. Which product features depend on SOAP calls?
  2. What is the revenue attached to those customers?
  3. Which customers are on Tier 1 concurrency (default 15 threads) versus higher tiers?
  4. Which customers use OneWorld (multi-subsidiary) or multi-currency?
  5. Which customers have heavy custom-field usage that requires per-account mapping overrides?

The output is a heatmap: customers × features × complexity. This drives your pilot selection and rollout ordering in later phases.

Phase 1: Read Migration to SuiteQL (Tests and Validation)

Reads represent 70-90% of SOAP call volume for most integrations. Migrate them first. Budget 4-8 weeks depending on inventory size.

Rewrite SOAP Reads as SuiteQL Queries

Start with the highest-volume, lowest-difficulty green endpoints from your inventory. For each one:

  1. Identify the source SOAP call's fields and filters.
  2. Locate the corresponding SuiteQL table (usually the lowercase record name: vendor, customer, transaction, transactionline).
  3. Write the SuiteQL query with explicit column selection - never SELECT * in production.
  4. Add feature-adaptive WHERE and JOIN logic based on multi_currency and multi_subsidiary context detected at connection time.
  5. Format dates at the SQL layer with TO_CHAR(field, 'MM/DD/YYYY') and normalize to ISO 8601 in the mapping layer.

A SOAP getList(Vendor) with subsidiary and currency data becomes:

SELECT
  v.id, v.entityid, v.companyname, v.email, v.phone,
  BUILTIN.DF(v.subsidiary) AS subsidiary_name,
  c.symbol AS currency_code,
  ea.addr1, ea.city, ea.state, ea.zip,
  TO_CHAR(v.lastmodifieddate, 'MM/DD/YYYY') AS modified_at
FROM vendor v
  LEFT JOIN entityaddress ea ON v.defaultbillingaddress = ea.nkey
  LEFT JOIN subsidiary s ON v.subsidiary = s.id
  LEFT JOIN currency c ON v.currency = c.id
WHERE v.isinactive = 'F'
  AND v.lastmodifieddate >= ?
ORDER BY v.lastmodifieddate DESC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY

If the account is single-currency, the mapping layer skips the currency JOIN and hardcodes USD. If single-subsidiary, the subsidiary JOIN is omitted and the response defaults to primary subsidiary. This is the feature-adaptive query pattern applied concretely.

Test Case Template: Read Parity

For every SuiteQL query, write parity tests before touching production. Template:

test_id: vendor_list_multi_currency_oneworld
description: "Vendor list on a OneWorld multi-currency account"
setup:
  account: sandbox_oneworld_multicurrency
  seed_data: 500 vendors, 3 subsidiaries, 5 currencies
inputs:
  updated_since: "2026-01-01T00:00:00Z"
  page_size: 100
assertions:
  - suiteql.record_count == soap.record_count
  - set(suiteql.records[*].id) == set(soap.records[*].id)
  - suiteql.records[0].subsidiary_name is not null
  - suiteql.records[0].currency_code matches ^[A-Z]{3}$
  - suiteql.p95_latency_ms < 800
tolerances:
  field_diff_allowed: ["last_synced_at"]
  numeric_epsilon: 0.001

Run this test against a sandbox with the same data reachable via both SOAP and SuiteQL. Automate the diff so any regression fails a CI check.

Validation Checklist for Every Migrated Read

Before flipping any read from SOAP to SuiteQL in production:

  • Record count matches SOAP output for identical filters
  • Field values match on a random 1% sample (>=100 records)
  • Pagination reaches the same total records
  • Query completes in under 30 seconds at p95
  • Works on single-subsidiary and multi-subsidiary sandbox accounts
  • Works on single-currency and multi-currency sandbox accounts
  • Handles empty result sets gracefully
  • Handles inactive/deleted records correctly
  • Custom field values (custentity*, custbody*, custcol*) reconcile against SOAP

Phase 2: Write Migration to REST Record API

Writes are lower volume but higher risk. A broken read misses data. A broken write corrupts it. Budget 3-6 weeks.

Map Each SOAP Write to Its REST Endpoint

The REST record endpoint pattern is POST/PATCH/DELETE /services/rest/record/v1/{recordType}/{id?}. For each SOAP add, update, or delete in your inventory:

SOAP Operation REST Equivalent Notes
add(Vendor) POST /services/rest/record/v1/vendor Returns 204 with Location header holding new ID
update(PurchaseOrder) PATCH /services/rest/record/v1/purchaseOrder/{id} Partial updates supported
delete(Customer) DELETE /services/rest/record/v1/customer/{id} 204 on success
add(PurchaseOrder) with lines POST /services/rest/record/v1/purchaseOrder with nested item.items [] Line items go inline in the payload
upsert(Vendor) by external ID Lookup + PATCH or POST REST has no native upsert; implement client-side

Handle the Batch Write Gap

Until 2026.1, REST writes are single-record. If your SOAP integration relied on batches of 100-1000 records, you have two options:

  1. Wait for 2026.1 batch REST if your customers can be upgraded and downtime is acceptable. The new homogeneous batch REST endpoint accepts up to 1,000 same-type operations per async request.
  2. Fan out writes with disciplined concurrency on older releases. Cap in-flight writes at (customer_concurrency_limit - 2) to leave headroom for reads. Use exponential backoff with jitter on 429s.

Idempotency Is Non-Negotiable

SOAP upsert operations had implicit idempotency via external IDs. REST does not. Every write path must implement:

  • Client-generated idempotency keys stored per operation (external_ref, request hash, or explicit Idempotency-Key header where supported).
  • Deduplication on retry. If a POST times out, look up whether the record was actually created before retrying.
  • External ID mapping table - track NetSuite internal IDs against your application's business keys so retries do not create duplicates.

Test Case Template: Write Round-Trip

test_id: vendor_create_round_trip
description: "Create vendor via REST, verify all fields via SuiteQL"
inputs:
  payload:
    entityid: "TEST-VENDOR-{{uuid}}"
    companyname: "Acme Corp"
    email: "ap@acme.example"
    subsidiary: { id: "1" }
    currency: { id: "1" }
    custentity_partner_tier: "gold"
assertions:
  - rest.status == 204
  - rest.location matches /vendor/\d+$
  - suiteql_lookup.entityid == payload.entityid
  - suiteql_lookup.custentity_partner_tier == "gold"
  - retry_with_same_key.creates_zero_duplicates
cleanup:
  - DELETE /services/rest/record/v1/vendor/{id}

Write Migration Validation Checklist

  • Every create returns a resolvable NetSuite internal ID
  • PATCH partial updates do not clobber unspecified fields
  • DELETE is idempotent (repeated deletes return the same status)
  • Custom field writes reconcile via SuiteQL lookup
  • Retry on 429 does not create duplicate records
  • Line-item write ordering is preserved on read-back
  • Multi-currency writes respect the account's base currency conversion

Phase 3: SuiteScript RESTlet Deployments for Gaps

Some SOAP capabilities have no direct SuiteQL or REST replacement. Phase 3 deploys custom SuiteScript into each customer account to close these gaps. Budget 2-4 weeks of engineering plus deployment coordination time per customer.

Identify the Gap List

From your Phase 0 inventory, the red-scored endpoints go here. Common gaps:

  • PDF rendering (render.transaction() for Purchase Orders, Invoices, Sales Orders)
  • Dynamic form field metadata (runtime field IDs, mandatory flags, select options)
  • Full sales tax item records (nested tax type references not exposed by SuiteQL)
  • File cabinet operations beyond the basic REST file endpoint
  • Saved search execution with complex per-account criteria

Suitelet Design Pattern

A single Suitelet can multiplex several capabilities via an entity query parameter. This limits the deployment footprint to one script per customer account instead of one per feature.

// One Suitelet, many entities
define(['N/render', 'N/record'], (render, record) => {
  const onRequest = (context) => {
    const entity = context.request.parameters.entity || 'purchase_order'
    switch (entity) {
      case 'purchase_order_download':
        return renderPurchaseOrderPdf(context)
      case 'purchase_order':
      case 'vendor':
        return getFieldMetadata(context, entity)
      case 'purchase_order_item':
      case 'purchase_order_expense':
        return getSublistFieldMetadata(context, entity)
      default:
        context.response.setHeader({ name: 'Content-Type', value: 'application/json' })
        context.response.write(JSON.stringify({ error: 'unknown entity' }))
    }
  }
  return { onRequest }
})

Accept a defaultValues JSON parameter and pass it into record.create() so field introspection reflects form-specific defaults (e.g., which departments are visible for a given subsidiary selection).

Deployment Rollout Across Customer Accounts

Each Suitelet must be installed into every customer's NetSuite account. This is operational work, not just engineering. Coordinate with:

  • NetSuite administrators at each customer for script upload and deployment record creation
  • Change management approvers at regulated customers (finance controls, SOX documentation)
  • Version tracking - store the deployed script version per account so you can plan rolling updates

Automation options: bundle the Suitelet as a NetSuite SuiteApp for self-service install, or expose deployment as a guided wizard in your product's admin UI. On first connection, parse the deployed Suitelet URL into its script ID, deployment ID, and path components and store those in your account context so every subsequent call can address the right script instance.

Suitelet Validation Checklist

  • Script deploys cleanly on OneWorld and standard accounts
  • PDF output is byte-identical to what customers see in the NetSuite UI
  • Field metadata reflects form-level visibility rules (not just record-level)
  • Select-option lists respect current record state via defaultValues
  • Errors surface as structured JSON with actionable messages
  • Governance units per invocation stay under 1,000 (NetSuite script limit)
  • Deployment record permissions restrict execution to your integration's role

Testing, Rollout, Rollback, and Monitoring Checklist

The migration succeeds or fails on your operational discipline more than your architectural choices. Use this master checklist for every phase.

Pre-Deployment Testing Checklist

  • Parity tests pass on sandbox for every migrated endpoint
  • Load test at 2x expected peak concurrency on the customer's tier
  • Error-injection tests: simulate 429, 500, 401, and timeout responses
  • TBA signature verification against known-good OAuth 1.0 test vectors
  • Feature-adaptive queries validated on all four edition combinations (OneWorld × multi-currency)
  • Custom field mappings validated against a representative production customer's schema
  • Date and timezone handling tested against records from multiple fiscal years
  • Reconciliation job compares shadow-table output against legacy authoritative output

Staged Rollout Procedure

  1. Feature flag defaulted to false for all customers.
  2. Enable shadow mode for a pilot cohort: the new path writes to a shadow table, the legacy path remains authoritative.
  3. Run 30-day reconciliation targeting >=99.9% record match. Root-cause every discrepancy as either a mapping bug or a legacy artifact.
  4. Cohort cutover: flip the flag for the pilot cohort (5-10% of customers) during a low-traffic window.
  5. Monitor at 5-minute granularity for the first 6 hours. Expand to hourly granularity for the first 72 hours.
  6. Expand in cohorts of 10-20% with 48-72 hours between each. Watch for platform-specific issues that only appear at volume.
  7. Full cutover with a 6+ month buffer before the 2028.2 hard deadline.

Rollback Playbook

If any of these signals trigger, roll back immediately:

  • Error rate on the new path exceeds baseline by 2x for 15 consecutive minutes
  • P95 latency exceeds baseline by 3x for 15 consecutive minutes
  • Reconciliation delta jumps above 0.5% on any migrated resource
  • Any customer reports data corruption or missing records

Rollback procedure:

  1. Flip the feature flag to false for the affected cohort. The legacy path resumes as authoritative.
  2. Because shadow mode ran in parallel, no data reconstruction is needed. The rollback is lossless.
  3. Snapshot the shadow-table state for forensics.
  4. Root-cause offline before scheduling another cutover attempt.

Ongoing Monitoring After Cutover

Track these signals continuously in your dashboards, with alerts wired to on-call:

  • Concurrency headroom per account - alert when in-flight requests exceed 80% of the customer's tier limit
  • 429 rate per account - a rising 429 rate signals a hot customer that needs concurrency tuning or SuiteCloud Plus
  • SuiteQL query p95 latency by resource - regressions here signal upstream schema drift or missing indexes
  • RESTlet governance unit usage - approaching NetSuite's 1,000-unit limit means the Suitelet needs refactoring
  • TBA signature failure rate - a spike almost always means clock drift or credential rotation issues
  • Reconciliation gap sampling - a small percentage of records reconciled daily against direct SOAP calls until SOAP is decommissioned in 2028.2

Templates: Inventory Spreadsheet, Test Plan, and Phased Timeline

Copy these templates into your team's working docs and adapt them.

Template 1: SOAP Endpoint Inventory Spreadsheet

Column headers for a Google Sheet or Notion database:

Column Type Example
soap_operation enum getList
record_type string PurchaseOrder
daily_avg_calls number 1200
daily_peak_calls number 4800
fields_read list id, tranid, entity, total, status
fields_written list (empty for reads)
batch_size number 100
needs_sublists boolean true
consumer_service string invoice_sync_worker
revenue_attached_usd number 480000
difficulty_score enum green / yellow / red
replacement_surface enum suiteql / rest_record / restlet
replacement_target string SuiteQL query on transaction JOIN transactionline
owner_engineer string @jane
target_phase enum phase_1
status enum not_started / in_progress / shadow / cutover / decommissioned

Sort by daily_peak_calls DESC to see the highest-blast-radius endpoints. Filter by difficulty_score = red to build the Phase 3 backlog.

Template 2: Migration Test Plan

For each migrated endpoint, produce a test plan with these sections:

## Endpoint: <SOAP operation and record type>
 
### Scope
- Source SOAP call: <exact operation>
- Target replacement: <SuiteQL / REST / RESTlet>
- Owning engineer: <@handle>
- Target phase: <0/1/2/3>
 
### Test Environments
- Sandbox account IDs: <list>
- Feature coverage: OneWorld yes/no, multi-currency yes/no, custom form yes/no
 
### Parity Tests
- [ ] Record count identical for identical filter
- [ ] Field-level diff on 1% random sample
- [ ] Sublist / line-item counts match
- [ ] Pagination reaches same total
 
### Performance Tests
- [ ] P95 latency under <threshold> ms
- [ ] Sustained throughput at customer's concurrency tier
- [ ] No 429s at 80% of tier limit
 
### Failure-Mode Tests
- [ ] Correct behavior on 429 (backoff, no cascade)
- [ ] Correct behavior on 500 (retry with jitter)
- [ ] Correct behavior on 401 (token refresh path, alert)
- [ ] Timeout handling with idempotent retry
 
### Rollback Criteria
- <specific thresholds tied to error rate, latency, reconciliation delta>
 
### Sign-Off
- [ ] Engineering lead
- [ ] QA lead
- [ ] On-call rotation notified

Template 3: Sample Phased Timeline (Team of 2-3 Engineers)

Month Milestone Deliverable
M1 Phase 0 kickoff SOAP inventory complete, difficulty scored, customer heatmap done
M2 Phase 1 begins Read migration on top 5 highest-volume endpoints in sandbox
M3 Phase 1 continues All green-scored reads migrated, parity tests green
M4 Phase 1 pilot Shadow mode running for 3 pilot customers, reconciliation instrumented
M5 Phase 2 begins Write migration on lowest-risk endpoints (vendors, customers)
M6 Phase 2 continues All write paths migrated with idempotency, shadow mode running
M7 Phase 3 begins RESTlet designed, deployed to 3 pilot accounts
M8 Phase 3 scale RESTlet rolled out to 25% of customer base
M9 Cutover cohort 1 10% of customers cut over from legacy SOAP
M10 Cutover cohorts 2-3 50% cumulative cutover
M11 Cutover cohort 4 100% cutover, legacy SOAP path in shadow only
M12 Decommission Legacy SOAP code removed, monitoring wound down

Compress this timeline if you have more engineers or a smaller integration surface, but do not skip the shadow-mode and reconciliation windows. Those are what prevent silent data corruption.

From Point-to-Point to Unified: Migrating Multiple Accounting Integrations

If you already ship NetSuite alongside QuickBooks, Xero, and Zoho Books as separate point-to-point connectors, you know what a treadmill this becomes. Each API has its own auth scheme, pagination model, error format, and field naming. Every schema change means a bespoke fix. This section is the playbook for consolidating those integrations behind a single unified accounting API without breaking production customers.

The goal: no custom APIs for accounting integrations. One code path in your product, one contract, one set of tests. The unified layer handles the provider-specific quirks. This is the practical shape of a unified accounting software integration strategy.

When to Migrate to a Unified API (Signals and Thresholds)

Not every team should abandon their point-to-point integrations. Migrate when you hit any of these signals:

  • Three or more accounting integrations in production. Maintenance cost grows non-linearly. At three connectors, you spend more time firefighting than shipping.
  • A dedicated FTE (or more) maintains integrations. If someone on your team is full-time chasing rate limits, refresh token failures, and field mapping changes, you have crossed the break-even point.
  • Customer demand for a new ERP arrives every quarter. Sales wants Sage Intacct. Ops wants Zoho. Enterprise wants SAP. If you cannot ship a new integration in under two weeks, you will lose deals.
  • Your data model has diverged from any single ERP. You have already built internal abstractions (a "unified invoice", a "unified vendor") on top of provider-specific responses. That is a unified API - just poorly factored and locked inside your codebase.
  • Onboarding a new integration engineer takes months. Domain knowledge is trapped in per-integration handler files. The team cannot scale.
  • API-driven revenue is meaningful. If integrations are a paid feature or gate enterprise deals, the reliability floor matters more than the flexibility ceiling.

If none of these apply - you have one integration, low customer volume, stable requirements - stay point-to-point. The unified strategy has real upfront cost.

Pre-Migration Audit: Inventory of Customer Accounting Platforms

Before touching any code, catalog what you have. This audit becomes the source of truth for the migration.

Build a spreadsheet or internal doc that captures:

  1. Every accounting platform in production. QuickBooks Online, Xero, NetSuite, Sage Intacct, Zoho Books, Wave. Note the customer count per platform.
  2. Which resources you actually use. Invoices, bills, vendors, customers, chart of accounts, journal entries, payments, tax rates. Do not migrate resources you do not touch.
  3. Auth scheme per platform. OAuth 2.0, OAuth 1.0 TBA, API key. Note token TTLs and refresh patterns.
  4. Every custom field mapping. For each customer, list the custom fields you read or write. This is where migrations go wrong - customer-specific mappings hidden in code.
  5. Sync frequency and volume. Real-time webhooks, hourly polls, nightly batch. Estimate records synced per day per customer.
  6. Known edge cases. Multi-currency, multi-subsidiary, tax jurisdictions, custom transaction types. Anything you had to special-case.
  7. Current error rates and SLAs. Baseline metrics you will compare against post-migration.

The output is a compatibility matrix: for each accounting platform, does your target unified schema cover every resource and field you need? Any gaps become either extensions to the unified schema, per-customer overrides, or reasons to keep a specific customer on the legacy connector during phase one.

Pilot Plan: Select Customers and Scope

Pick pilot customers deliberately. The wrong pilot burns political capital and slows the whole migration.

Ideal pilot profile:

  • Uses one of your most common accounting platforms (QuickBooks Online or Xero is a safe start).
  • Has moderate transaction volume - enough to stress the system, not enough to make failures catastrophic.
  • Has an engaged technical contact who will report issues quickly.
  • Runs a workflow you can validate objectively (e.g., invoice sync where record counts and totals must match).
  • Is not a top-5 revenue customer. Do not learn on your biggest accounts.

Scope the pilot tight:

  • One accounting platform.
  • One or two resources (start with invoices and customers).
  • Read-only sync first. Writes come in a later phase.
  • A defined success criterion: "For 30 days, unified API output matches legacy connector output on 99.9% of records, with reconciliation deltas explained."

Do not pilot across three platforms simultaneously. You will not know which one caused the problem.

Step-by-Step Migration: Mapping, Parallel Sync, Reconciliation

Once the pilot is scoped, execute in this order:

Step 1: Define unified schema mappings. For each resource, write a declarative mapping between the unified schema and the provider's native format. If your unified API platform supports JSONata or a similar expression language, this is where custom fields, per-account overrides, and computed values live. Do not embed this logic in application code.

Step 2: Wire up the unified integration behind a feature flag. In your product, add a per-customer feature flag (use_unified_accounting_api) defaulting to false. The new code path reads from the unified API when enabled, legacy connector otherwise.

Step 3: Run parallel sync in shadow mode. For pilot customers, enable both paths. The legacy connector remains authoritative and writes to your database as before. The unified path runs in parallel, writes to a shadow table, and never affects customer-visible state.

Step 4: Reconcile. Run a nightly job that diffs shadow-table records against the authoritative table. For invoices, compare id sets, amounts, statuses, and line-item counts. For every mismatch, log the discrepancy and root-cause it: is the unified mapping wrong, or is the legacy connector missing data?

Step 5: Iterate mappings until reconciliation is clean. Target 99.9% agreement over a 30-day window. Discrepancies over that threshold usually cluster around a handful of edge cases (rounding, timezone handling, deleted-record semantics). Fix them as mapping overrides, not code changes.

Step 6: Extend to writes. Once reads reconcile, add writes. For each create/update operation, send the payload through both paths and diff the resulting third-party record. This is more expensive to validate but catches idempotency and field-coercion bugs.

Worked Example: Migrating One Invoice-Sync Flow from QuickBooks

Concrete example. Your product currently pulls QuickBooks invoices via a custom connector using GET /v3/company/{realmId}/query?query=SELECT * FROM Invoice and stores them in your invoices table.

Before (point-to-point):

// legacy/quickbooksInvoiceSync.ts
const response = await quickbooksClient.query(
  `SELECT * FROM Invoice WHERE MetaData.LastUpdatedTime >= '${since}'`
)
for (const invoice of response.QueryResponse.Invoice) {
  await db.invoices.upsert({
    external_id: invoice.Id,
    customer_id: invoice.CustomerRef.value,
    total: invoice.TotalAmt,
    balance: invoice.Balance,
    due_date: invoice.DueDate,
    status: invoice.Balance === 0 ? 'PAID' : 'OPEN',
    line_items: invoice.Line.map(l => ({
      description: l.Description,
      amount: l.Amount,
    })),
  })
}

Every field mapping, status derivation, and line-item flattening is hardcoded. Adding Xero requires a parallel handler with completely different field names. Adding NetSuite means a third handler with SuiteQL queries and TBA signatures. This is exactly the point-to-point trap.

After (unified API):

// invoiceSync.ts
const response = await fetch(
  `https://api.truto.one/unified/accounting/invoices?integrated_account_id=${accountId}&updated_since=${since}`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
)
const { result: invoices } = await response.json()
for (const invoice of invoices) {
  await db.invoices.upsert({
    external_id: invoice.id,
    customer_id: invoice.contact.id,
    total: invoice.total_amount,
    balance: invoice.balance,
    due_date: invoice.due_date,
    status: invoice.status,
    line_items: invoice.line_items,
  })
}

The same code works for QuickBooks, Xero, NetSuite, and Zoho Books. Field-name normalization, status enum harmonization, and line-item structure are handled by the unified layer's mapping expressions.

Migration in practice:

  1. Enable use_unified_accounting_api=true for pilot customer A on the feature flag.
  2. Legacy sync writes to invoices. Unified sync writes to invoices_shadow for 30 days.
  3. Nightly reconciliation job runs: SELECT external_id, total, status FROM invoices EXCEPT SELECT external_id, total, status FROM invoices_shadow. Any rows returned are mismatches.
  4. Investigate. Common issues: QuickBooks returns TotalAmt as a positive number even for credit memos where your legacy logic flipped the sign. Fix with a JSONata override on that account, no code deploy.
  5. After 30 days at 99.9%+ reconciliation, flip the flag: unified path becomes authoritative, legacy path becomes shadow.
  6. After another 30 days with no regressions, remove the legacy path entirely.

Cutover and Rollback Plan

Cutover is not a big-bang event. It is a flag flip, and it must be reversible.

Cutover procedure:

  1. Announce a maintenance window (defensive - the actual customer impact should be zero).
  2. Freeze deploys for 24 hours before and after the flip to isolate any regressions.
  3. Flip use_unified_accounting_api from false to true for the target customer cohort.
  4. Monitor error rates, sync latency, and reconciliation deltas for the first 6 hours at 5-minute granularity.
  5. If any metric exceeds baseline by 2x, roll back immediately.

Rollback procedure:

  1. Flip the feature flag back to false. The legacy connector resumes as the authoritative writer.
  2. Because both paths ran in parallel during shadow mode, there is no data to reconstruct. The rollback is instant and lossless.
  3. Root-cause the failure offline before attempting cutover again.

Never migrate all customers in one flip. Roll out in cohorts of 10-20% at a time, with 48-72 hours between cohorts. This lets you catch platform-specific issues that only appear at volume.

Post-Migration Validation and Monitoring

Once cutover is complete, the work is not done. Ongoing validation is what keeps the unified layer honest.

Instrument these signals:

  • Reconciliation gap over time. Even after cutover, keep a sampled reconciliation job comparing unified API output against direct provider calls for a small percentage of records. A slowly widening gap indicates upstream schema drift.
  • Error rates per integration. Track 4xx and 5xx rates per accounting platform. A spike in QuickBooks 401s means the refresh token flow broke. A spike in NetSuite 429s means concurrency is exhausted.
  • Sync latency percentiles. P50, P95, P99 for full customer sync. The unified layer should not add more than 100-200ms of overhead per call.
  • Custom field coverage. For customers with heavy customization, track how many custom fields are read vs written successfully. Silent drops here cause quiet data loss.
  • Rate limit headroom. Normalized rate limit headers (ratelimit-remaining) should be logged and alerted on when they drop below a threshold per account.
  • Reconciliation delta by customer. Any single customer with persistent delta noise is a signal that their configuration needs a specific override.

Set up dashboards for each of these before you cut over the first customer. Do not build monitoring after the fact.

Adopting this approach is not just about writing less code. It is about moving integration behavior out of your critical path and into declarative configuration that anyone on your team can inspect, modify, and validate. That shift is what makes multi-ERP support sustainable at scale.

Truto vs Prismatic: Choosing Your Integration Platform for the NetSuite Migration

Once you understand the SuiteQL-first architecture, the next question is: what platform actually executes it? Two categories dominate the B2B SaaS integration space in 2026 - embedded iPaaS platforms like Prismatic and declarative unified APIs like Truto. They solve different problems at different architectural layers, and the NetSuite SOAP deprecation exposes exactly where those differences matter.

Prismatic is a workflow orchestration engine. It provides a low-code visual designer and a TypeScript SDK for building multi-step integration flows. Each integration is a distinct workflow artifact with triggers, steps, branches, and custom logic. Truto is a declarative unified API where every integration - including NetSuite - is defined entirely as data (JSON configuration and JSONata expressions) with zero integration-specific code in the runtime. For a deep architectural comparison, see our full Truto vs Prismatic guide.

The right choice depends entirely on what your product needs from the NetSuite integration.

Decision Checklist: Embedded iPaaS vs Unified API for ERP Migration

Question If Yes: Prismatic If Yes: Truto
Do your end-users need to visually build custom NetSuite workflows inside your product?
Does your engineering team need normalized CRUD access across NetSuite, QuickBooks, Xero, and others through one API?
Do you need conditional branching and multi-step orchestration that varies per customer?
Do you want to add new ERP integrations without code deploys?
Is your primary use case letting customers configure their own automation triggers?
Do you need SuiteQL orchestration, TBA signature handling, and feature-adaptive queries managed for you?
Do you need to embed an integration marketplace for non-technical users?
Must customer ERP data never be stored on the integration platform?

5 Real-World Scenarios and Recommendations

Scenario 1: Your customers need to configure their own NetSuite sync rules.

Your customers operate in different industries, and each needs unique trigger-action sequences - "When an invoice is created in NetSuite with amount > $10,000, route for approval in our system and notify Slack."

Recommendation: Prismatic. This is user-defined orchestration logic. Prismatic's embedded workflow builder lets your customers wire up these flows without your engineering team building custom code for each one. Expect 2-4 weeks to build the initial NetSuite workflow template, plus ongoing per-workflow maintenance as the upstream API changes.

Scenario 2: Your SaaS product needs to read and write accounting data across NetSuite, QuickBooks, and Xero through one API.

Your product powers dashboards, compliance reports, or financial analytics. Your engineering team should call GET /unified/accounting/invoices and get identical JSON whether the customer uses NetSuite or QuickBooks. You do not want to build separate SuiteQL queries, QuickBooks GraphQL calls, and Xero REST handlers.

Recommendation: Truto. The unified API normalizes the data model across all three platforms. Truto handles SuiteQL orchestration, OAuth 1.0 TBA signature math, and feature-adaptive queries behind a single endpoint. Implementation is typically days for the initial integration, not weeks.

Scenario 3: Non-technical staff need to deploy and manage NetSuite integrations.

Your customer success team needs to configure new customer NetSuite accounts, troubleshoot sync issues, and deploy integration instances without filing engineering tickets.

Recommendation: Prismatic. Prismatic's embedded marketplace and configuration wizards are purpose-built for this operational model. Technical support staff configure known integrations; engineers only get involved for new workflow types. Budget 4-8 weeks for initial setup and team training.

Scenario 4: Strict data residency or compliance requirements prohibit storing customer financial data on any third-party platform.

Your compliance team requires that customer ERP data never be persisted on the integration vendor's infrastructure. Every API call must pass through directly to the source.

Recommendation: Truto. Truto's pass-through architecture makes API calls directly to the underlying provider without caching or storing customer data. It is SOC 2 Type II and ISO 27001 compliant and also supports on-premises deployment. Prismatic stores integration execution data on its platform as part of its workflow engine, which may require additional compliance review for regulated industries.

Scenario 5: You are migrating from a homegrown SOAP integration and want to minimize re-engineering.

Your team has a working SOAP integration with hundreds of custom field mappings, multi-currency logic, and subsidiary routing. You need to get off SOAP with minimal disruption to your existing application architecture.

Recommendation: Truto. Your application code changes are minimal - swap the SOAP client for HTTP calls to the unified endpoint. Truto's three-level override hierarchy means your custom field mappings, subsidiary routing, and multi-currency logic can be reproduced as JSONata configuration without code deploys. Expect 1-3 weeks for migration depending on the complexity of your existing SOAP payloads.

Estimated Engineering Effort and Timelines

Task Prismatic (Embedded iPaaS) Truto (Unified API)
Initial NetSuite integration setup 2-4 weeks (build workflow, handle TBA auth, wire SuiteQL) Days (NetSuite config already exists)
Adding a second ERP (e.g., QuickBooks) 2-4 weeks (new workflow from scratch) Hours (same unified endpoint, new config)
Custom field mapping per customer Per-workflow TypeScript customization JSONata override, no code deploy
Handling NetSuite API breaking changes Update specific workflow, test, redeploy Update mapping configuration, no code deploy
Ongoing maintenance at 20+ integrations ~0.5 FTE dedicated Near-zero, config-only changes
Non-engineering staff training 2-4 weeks (visual designer, marketplace) Minimal, API-first and engineers self-serve

Migration and Maintenance Considerations

If you are currently on a homegrown SOAP integration or re-evaluating your middleware stack, keep these factors in mind:

Switching from homegrown SOAP to Prismatic: You are replacing custom SOAP code with custom workflow code. The net benefit is Prismatic's managed infrastructure (monitoring, retries, deployment tooling) and its embedded marketplace for customer self-service. The trade-off is that you still build and maintain per-integration workflow logic. For NetSuite specifically, your team still needs to understand SuiteQL, TBA authentication, and concurrency limits. Prismatic provides the execution environment, not the NetSuite-specific query orchestration.

Switching from homegrown SOAP to Truto: You are replacing custom SOAP code with a single API call. The platform handles SuiteQL orchestration, TBA signature generation, feature-adaptive queries, and concurrency management. Your engineering team does not need deep NetSuite domain expertise. The trade-off is that Truto does not provide end-user workflow builders or embedded marketplaces. If your product requires customers to configure their own multi-step automation flows, you will need a separate tool for that layer.

Using both: Some teams use Truto for core normalized data access - syncing invoices, vendors, and purchase orders across ERPs - and layer Prismatic on top for customer-specific automation workflows that operate on that data. This is a valid architecture if your product genuinely needs both capabilities.

Securing Your NetSuite Infrastructure for 2028

The SOAP shutdown is not a distant problem. The countdown is already running, and the degradation of SOAP performance has already begun. Engineering teams that migrate to a SuiteQL-first architecture today will secure a massive reliability advantage over competitors still wrestling with legacy XML.

If you are a B2B SaaS company building NetSuite integrations for your customers—particularly if you need to build ERP integrations without storing customer data—the complexity multiplies. Handling OAuth 1.0 TBA math, SuiteQL orchestration, polymorphic routing, and feature-adaptive queries across dozens of unique customer accounts is a significant engineering investment. To go deeper on abstracting this architecture, read our guide on how to integrate the Oracle NetSuite API without SOAP complexity or explore enterprise auth patterns to see how declarative configuration solves the TBA signature problem out of the box.

FAQ

What is the NetSuite SOAP API deprecation timeline?
Oracle is phasing out SOAP in stages: the last SOAP endpoint ships in 2025.2, no new SOAP features after 2026.1, older endpoints retire in 2027.2, and all SOAP endpoints are permanently disabled in the 2028.2 release.
Why is a direct SOAP-to-REST migration a mistake for NetSuite?
The SuiteTalk REST API is designed for single-record CRUD, not bulk extraction. It forces N+1 sub-resource fetches, lacks ORDER BY support, and has hard pagination ceilings. A 1-to-1 migration will spike API call volume and trigger concurrency throttling.
What is the tri-partite architecture for replacing NetSuite SOAP?
Use SuiteQL for complex reads and JOINs, the SuiteTalk REST API for single-record writes, and SuiteScript RESTlets for capabilities absent from both (like PDF generation and dynamic form metadata).
When should I choose Prismatic over Truto for NetSuite migration?
Choose Prismatic if your product needs to let end-users visually build custom multi-step NetSuite automation workflows, or if non-technical staff need to deploy and manage integration instances through an embedded marketplace.
When should I choose Truto over Prismatic for NetSuite migration?
Choose Truto if your engineering team needs normalized CRUD access across NetSuite, QuickBooks, and Xero through one API, if you want SuiteQL orchestration and TBA auth handled for you, or if compliance requires that customer data never be stored on the integration platform.
Can Truto and Prismatic be used together for NetSuite integrations?
Yes. Some B2B SaaS teams use Truto for core normalized data access (syncing invoices, vendors, purchase orders across ERPs) and layer Prismatic on top for customer-specific automation workflows that fall outside the common data model.

More from our Blog