INSIGHT · INTEGRATION
Running a NetSuite integration project: scoping, mapping, error handling and sign-off
How to run a NetSuite integration project: scope each flow, map fields and IDs, design error handling and retries, then test and sign off with finance.
A NetSuite integration rarely fails because the API call didn't work. It fails because nobody agreed which system owns the customer record, a field was mapped by guesswork, or an error sat unnoticed in a log until month-end. Those are project problems, not coding problems. They are solved by four pieces of work: scoping, mapping, error handling and sign-off.
I've led integrations on Celigo, Dell Boomi and native SuiteScript 2.1, with Coupa, Salesforce, HubSpot, Shopify, Stripe, Fortnox and others. This is the plan I follow, whichever tool carries the data. If you're still choosing the tool, I've covered that separately in native SuiteScript vs iPaaS.
Step 1: Scope the NetSuite integration flow by flow
"Integrate Salesforce with NetSuite" is not a scope. It is a dozen separate flows, each with its own rules. Break the integration into flows and write a short specification for each one. For every flow, answer these questions:
- Which records? For example, customers, sales orders, invoices, payments or vendor bills.
- Which system is the source of truth? One system owns each record type, or even each field. If both systems can edit the same field, decide which wins.
- Which direction? One way, or both. Two-way sync needs protection against echo loops, where an update in one system triggers an update back, and so on forever.
- What triggers it? Real time on save, a schedule, or a manual run.
- How many, and how often? Daily and peak volumes, and the busiest day of the month.
- What happens to history? Whether open or historical records are loaded at go-live, and how.
- Who owns errors? A named person or team, not "IT".
Two NetSuite limits belong in scope. First, every account has a concurrency limit on web services and RESTlet requests, which depends on your service tier and SuiteCloud Plus licences. Oracle documents that requests over the limit are rejected, and you can reserve part of the limit for a single integration at Setup > Integration > Integration Management > Integration Governance (Oracle: concurrency limit per integration). If three integrations all run at 2am, they compete for the same pool.
Second, give each application its own integration record. In Oracle's model each application can have its own record, listed at Setup > Integration > Manage Integrations. That lets you see each application's own execution log and block one application without touching the others (Oracle: integration record overview). Pair it with a dedicated integration role that has only the permissions the flow needs.
Then write down what is out of scope. Exclusions agreed early are far cheaper than exclusions argued about in UAT.
Step 2: Map identifiers first, then fields
Before you map a single amount, decide how each system will recognise a record it has seen before.
In NetSuite the usual answer is the external ID. You store the other system's ID on the NetSuite record, and the integration looks records up by it. NetSuite's REST web services support an upsert operation that uses it: a PUT request with the external ID in the URL creates the record if it doesn't exist and updates it if it does (Oracle: using the upsert operation). That is what makes a retry safe. Without it, a timeout followed by a retry gives you two invoices.
Write the other system's reference back as well, so a user in either system can trace a record to its partner. On the Cape.io Fortnox integration, the Fortnox document number was stamped back on every NetSuite transaction for exactly that reason.
Then map fields, and be explicit about the awkward ones:
- Subsidiary, currency and tax code. In OneWorld, a record in the wrong subsidiary is a consolidation problem, not a typo.
- GL accounts, departments, classes and locations. Map each value. Don't let an unmapped value fall back to a default account. In the Cape.io build, unmapped accounts fail loudly by design, so the statutory accounts still reconcile line by line.
- Items. Decide what happens when a line has no matching item. On the Jockey Club's Coupa integration, a line with no item match posts as an expense line coded from the billing string, rather than stopping the whole invoice.
- Dates, rounding and line order. Small differences here cause most reconciliation breaks.
Keep crosswalks, such as entity to subsidiary or tax code to tax code, as data the business can maintain, in a custom record or a lookup table. Hard-coded lists in scripts become a change request every time finance adds a code.
Step 3: Design NetSuite integration error handling before you build
Every integration will fail sometimes: an inactive supplier, a closed period, a blank mandatory field. What matters is what happens next. Agree these rules in design, not after the first bad month-end:
- Classify errors. Separate temporary errors (timeouts, concurrency rejections, an unavailable endpoint) from data errors (a missing vendor, a closed period). Temporary errors can retry automatically with a growing delay between attempts. Data errors need a person, and retrying them only fills the log.
- Make every write safe to repeat. Use upserts or a lookup on the external ID, and decide the order of writes. On the Jockey Club build, NetSuite IDs are written back to Coupa first and the exported flag second, because Coupa locks exported invoices.
- Log in one place, with context. Each error should record the flow, the source record, the message and a suggested fix. On that project, finance and support see every flow's health and every error in an integration control centre built as a Suitelet inside NetSuite, without logging in to Celigo.
- Alert the owner. Send failures to the named owner from Step 1, and alert on silence too. A flow that hasn't run for a day is often a bigger problem than one that failed once.
- Guard against bad runs. Before a large run, check a known record first. The Jockey Club chart-of-accounts flow checks a "golden record" combination before it starts, so a change to the segment order stops the run before it can do damage.
- Write the runbook. For each common error: what it means, who fixes it, and how to re-run.
Step 4: Test like finance will use it
Build and test in a sandbox, and plan for refreshes. Oracle notes that a sandbox refresh deletes the token key and token secret, so they have to be regenerated, and it recommends separate integration records in each account so sandbox changes can't break production (Oracle: TBA credentials and accounts). If your integration still uses token-based authentication, read my note on the move from TBA to OAuth 2.0 before you build.
Test in three layers:
- Unit tests of each mapping, run by the developer.
- System integration tests (SIT) end to end across both systems, with scripted cases for creates, updates, cancellations, credits, partial payments and each error you designed for in Step 3.
- User acceptance tests (UAT) run by the people who will live with it, using real scenarios from a recent month.
Test volume, too. Run a realistic month-end batch, not ten records. And if you're loading history, load it through the same flows and rules as live data. On the Jockey Club project, a 777-invoice backlog was re-imported through the live rules, and every remaining error was traced to a named data fix.
Step 5: Sign off against evidence
Sign-off is where projects either end cleanly or drift. Agree the criteria at the start, and make them measurable:
- Every SIT and UAT script passed, or has an agreed workaround and an owner.
- A reconciliation report that ties the count and value of records in both systems for a test period, by subsidiary.
- The runbook, mapping document and support contacts handed over.
- Alerts tested, by forcing a failure and checking the right person was told.
- A go-live plan: cut-off times, who switches each flow on, and how to roll back.
Signatures should come from the finance owner and the business owner of each flow, not just the developer. The first two or three month-ends after go-live are the real test, so agree who is on hand for them. Ongoing NetSuite support after that can be in-house or a retainer, but someone must own the error queue.
When an integration is already live and failing
If an existing NetSuite integration keeps breaking, work the same steps in reverse: list the flows, rebuild the mapping from what the integration actually does, then add the missing error handling. My fixed-price NetSuite Integration Rescue Sprint does that for Celigo, Boomi and SuiteScript integrations.
For a new project, my NetSuite integration services cover scoping, mapping, build, testing and sign-off. You can see how a 24-flow, nine-entity build came together in the Jockey Club case study.
Key takeaways
- Scope a NetSuite integration flow by flow. For each one, write down the source of truth, the direction, the trigger, the volume and who owns errors.
- Map identifiers before fields. An external ID on every integrated record is what lets a retry update a record instead of duplicating it.
- Design error handling as part of the build. Every failure should be logged, visible to the person who can fix it, and safe to retry.
- Sign off against agreed test scripts and reconciliation reports, signed by finance and the business owner, not just the developer.
Planning a NetSuite integration, or living with one that keeps failing? Book a call and I'll walk through your flows with you.
Need this done on your NetSuite?
Book a free 30-minute consultation. I'll review your setup and tell you honestly whether I'm the right fit.