Sync / roster

NetSuite ↔ Salesforce two-way sync

NSSFCRM NetSuite ──▶ Salesforce

Keep accounts, orders, and financial status consistent between NetSuite and Salesforce, with a clear system of record per object.

TypeSync / roster
Indicative timeline3–6 weeks
ComplexityHigh
DeliveryFixed-scope
  • A defined system of record per object
  • Accounts, orders and status synced both ways
  • Conflicts resolved deterministically
netsuite salesforce crm erp two-way sync integration
How it works

Sales works in Salesforce; finance works in NetSuite; both think they own the customer. Without a defined system of record per object, a two-way sync becomes a loop of overwrites. This integration establishes ownership per field, syncs accounts, orders, and status both ways, and resolves conflicts deterministically.

Free reference snippet

Yours to use

A working piece from this integration — no sign-up. The full build handles the edge cases, safeguards, and cutover.

NetSuite RESTlet response → Salesforce Account/Order field mapping (System of Record: NetSuite for Orders & Finance):

// NetSuite RESTlet GET /record/customer/{id} response shape
{
  "id":           "NS-CUST-88421",
  "companyName":   "Acme Manufacturing",
  "externalId":   "SF-001xx00000D7lHAAS",  ← Salesforce Id
  "currency":      "USD",
  "creditLimit":   50000,
  "balance":       12400.50,
  "stage":         "CUSTOMER",                → Account.Type
  "lastOrderDate":  "2025-01-14"
}
NetSuite FieldSalesforce ObjectSalesforce FieldNotes
idAccountAccountNumberNS internal ID
companyNameAccountName
balanceAccountAnnualRevenueSync direction: NS→SF
creditLimitAccountCreditLimit__cCustom field
stageAccountTypeMap: CUSTOMER→Customer
externalIdAccountExternal_Id__cCross-reference key

Implementation pathway

Step by step

How we'd take this from discovery to a production-safe cutover — the phases, the canonical mapping, and the edge cases that bite.

This integration maintains bidirectional consistency for Account/Customer records and Order/SalesOrder records between NetSuite and Salesforce, with NetSuite serving as the system of record for financial transactions and Salesforce for CRM activities. The sync uses NetSuite's SuiteTalk REST/Web Services APIs and Salesforce's REST and Composite APIs, coordinated through a middle-layer orchestration service.

Implementation Phases

  1. Environment preparation. Register a Salesforce Connected App with OAuth 2.0 client credentials flow. Create a NetSuite integration record with TBA (Token-Based Authentication). Provision sandbox environments on both sides; document current record counts and field lengths as baseline.
  2. Object and field audit. Map Customer.entityIdAccount.AccountNumber; map Customer.companynameAccount.Name. For orders, map SalesOrder.externalIdOrder.ExternalId__c and SalesOrder.statusOrder.Status. Capture all custom fields used in each org. Validate that Account.BillingAddress and Customer.billingaddress share the same latitude for geocoding if used.
  3. Conflict-resolution logic. Implement a priority rule: for Account.Name and Customer.companyname, NetSuite wins on create; for Order.Status changes, NetSuite wins because it holds financial truth. Store last-modified timestamps in Customer.lastCustomSyncTime__c and Account.LastModifiedDate to detect drift. Use optimistic locking via SalesOrder.version / Order.SystemModstamp.
  4. Delta-sync pipeline. Build a scheduled job (every 15 minutes) that queries Customer where lastModifiedDate > :lastSyncTimestamp using NetSuite SuiteQL SELECT internalId, companyname, email, entityStatus, lastModifiedDate FROM customer WHERE lastModifiedDate > '{ts}'. Push changed records to Salesforce upsert on Account.External_Id__c. Mirror the opposite direction for Opportunity closed-won triggers that generate SalesOrder in NetSuite.
  5. Idempotency and error handling. Use SalesOrder.externalId (format: SF-{Opportunity.Id}) as the idempotency key. On HTTP 409 (conflict), fetch both versions, apply priority rules, and re-upsert. Queue failures to a dead-letter table (Sync_Error_Log__c / CustomRecordSyncFailures) with error_code, payload, and retry_count. Retry with exponential backoff up to 5 attempts.
  6. Transformation layer. Translate Salesforce Account.Type enum (Customer, Partner, Prospect) to NetSuite Customer.category via a lookup map. Translate Opportunity.StageName "Closed Won" to SalesOrder.status "Pending Fulfillment". Normalize phone formats using E.164 before write.
  7. Unit and integration testing. Test create, update, and delete flows end-to-end in sandbox. Verify that updating Customer.terms in NetSuite reflects in Account.NetSuite_Payment_Terms__c in Salesforce within one sync cycle. Validate that deleting a Customer in NetSuite sets Account.Active__c = false in Salesforce (soft-delete pattern).
  8. UAT and data reconciliation. Run a full extract of all Customer records from both systems; join on externalId; report mismatches on companyname, email, entityStatus. Fix discrepancies before go-live. Reconcile order totals: SalesOrder.total vs. Order.TotalAmount for all records created in the last 90 days.

Field Mapping

NetSuite Field Salesforce Field Direction Transform Rule
Customer.internalId Account.NetSuite_Id__c NS → SF Write on insert; read-only thereafter
Customer.externalId Account.External_Id__c Bidirectional Upsert key; format: SF-{Account.Id} on SF side
Customer.companyname Account.Name NS wins on create Coalesce if blank; prefer NS on conflict
Customer.email Account.BillingEmail__c Bidirectional Validate RFC 5322; skip nulls
SalesOrder.externalId Order.ExternalId__c Bidirectional Idempotency key; format: SF-{Opportunity.Id}
SalesOrder.total Order.TotalAmount NS → SF Read-only in SF; NS is financial system of record
SalesOrder.status Order.Status NS → SF Map: BilledActivated; Pending FulfillmentDraft
Customer.entityStatus Account.CustomerStatus__c NS → SF Reference CustomerStatus custom list in NS

Edge Cases

  • Duplicate Account.Name. Salesforce allows duplicate Account.Name values; NetSuite requires unique Customer.companyname. If a sync attempt creates a duplicate in NS, the API returns SSS_UNIQUE_NAME_REQUIRED. Handle by appending Account.AccountNumber suffix or raising an exception for manual resolution.
  • Closed-won Opportunity race condition. If a user updates an Opportunity.StageName to "Closed Won" in Salesforce while the delta-sync job runs simultaneously from NS, both systems may attempt to create SalesOrder. Use a database-level lock on Opportunity.Id and check Order.ExternalId__c existence before insert.
  • Field length overflow. Account.Name max is 255 chars; Customer.companyname max is 83 chars. Truncate with ellipsis and log a warning to Sync_Warning_Log__c when source exceeds 80 chars.
  • Null handling on SalesOrder.total. If SalesOrder has no line items, total may be null. Map null to Order.TotalAmount = 0 and set Order.HasErrors__c = true in Salesforce.
  • Deleted record orphaning. When a Customer is deleted in NetSuite (hard delete), Salesforce Account retains the record. Implement a soft-delete pattern: set Account.Active__c = false in NetSuite before deletion, and sync that flag as the signal to deactivate in Salesforce.
  • Timezone drift. SalesOrder.trandate is stored in NetSuite's configured timezone; Order.EffectiveDate in Salesforce is UTC. Apply timezone offset using userTimeZone from the initiating user record to avoid off-by-one-day order dates.

Cutover

Cutover must be fully reversible and reconciled. Before the live cutover window, perform a final full extract from both systems and generate a reconciliation report keyed on externalId. Any record present in one system but not the other must be manually reviewed; do not auto-create orphans during cutover. Enable the delta-sync scheduler in "shadow mode" for 24 hours: capture changes but do not write to the target, then compare the shadow log against the live log to verify no data loss. Establish a rollback procedure: disable the sync scheduler, re-enable direct manual entry in both systems, and run a delta extract to capture any records modified during the cutover window. Set the initial lastSyncTimestamp to the cutover completion time, not a past time, to prevent replaying historical changes as new updates. Designate a single on-call engineer for each system during the 72-hour post-cutover stabilization period, with authority to halt sync and revert to manual mode if error rates exceed 1% of total volume.

What a full implementation includes

  • Canonical mapping between NetSuite and Salesforce, to the field level.
  • The edge cases that corrupt data at cutover — identified, handled, and tested.
  • Production-safe rollout: reversible, phased, with reconciliation checks.
  • Handover documentation your team can operate from.

Build this against your estate

Reading the reference is free. Delivering it under liability — with the safeguards that keep production running through the cutover — is what we do.

$9,000–$26,000
Contact us