
Multi-Tenant ERP Integrations for AI Products: Why NetSuite, SAP, and Sage Each Break Your Architecture Differently
Why multi-tenant ERP integrations break across NetSuite, SAP, and Sage and how modern integration infrastructure fixes it

Chris Lopez
Founding GTM
Multi-Tenant ERP Integrations for AI Products: Why NetSuite, SAP, and Sage Each Break Your Architecture Differently
If your engineering team has built CRM integrations and feels confident about ERP, you're about to learn an expensive lesson. CRM APIs, despite their quirks, share a rough consensus on data models, REST endpoints, and OAuth-based authentication. ERPs share almost nothing. NetSuite, SAP Business One, and Sage Intacct each present fundamentally different API paradigms, authentication patterns, data models, and rate limiting behaviors. A multi-tenant integration that works flawlessly against NetSuite will fail in ways you didn't anticipate against SAP, and Sage will break it in a third, entirely unrelated way.
This matters now more than ever because AI products increasingly need ERP data. Financial agents that forecast cash flow, procurement automation that optimizes vendor spend, revenue recognition tools that close the books faster: all of them need deep, bidirectional access to their customers' ERP instances. And "their customers" means hundreds of tenants, each running a differently configured instance of a differently architected ERP system.
Engineering leaders we've advised consistently underestimate this problem by 3-5x. The CRM integration that took two months becomes an ERP integration that takes eight. The architecture that handled Salesforce multi-tenancy gracefully buckles under NetSuite's concurrency model, SAP's session management, and Sage's XML-only API surface. This is the last 10% problem at industrial scale: every customer's ERP is configured differently, and those differences aren't edge cases. They're the entire problem.
NetSuite: Where Concurrency Limits and Custom Records Collide
NetSuite is the most common ERP target for SaaS products selling into the mid-market, and it's also the one that creates the most architectural surprises. The fundamental challenge is that NetSuite exposes two distinct API surfaces with different capabilities, different performance characteristics, and different authentication requirements.
The SuiteTalk SOAP API is NetSuite's legacy integration interface. It supports the broadest set of operations, including saved searches, which are NetSuite's most powerful data retrieval mechanism. Saved searches let administrators define complex, multi-join queries that pull data across record types with filtering, sorting, and formula columns. Many NetSuite customers rely on saved searches as their primary reporting layer, which means your integration needs to execute them to access the data your customers actually use. The SOAP API handles this. The REST API does not fully replicate saved search capabilities, which means you often can't avoid SOAP even if you prefer REST.
The REST API, introduced as NetSuite's modern integration surface, supports SuiteQL (a SQL-like query language) and provides cleaner JSON payloads. It's faster for simple CRUD operations and better for building integrations that feel modern. But it lacks feature parity with SOAP in several critical areas. Custom transaction types, certain advanced search capabilities, and some record type operations are SOAP-only. Teams that build exclusively on REST discover these gaps in production when a customer's configuration requires a capability that only SOAP provides.
The concurrency problem is where multi-tenancy gets painful. NetSuite enforces a hard limit of 5 to 10 concurrent API requests per account (depending on license tier). This limit is per-account, not per-integration. If your customer runs other integrations, scheduled scripts, or SuiteScript automations, they consume from the same concurrency pool. Your integration doesn't just compete with itself; it competes with everything else running in that account. At scale, with hundreds of tenants, you need request queuing, backoff strategies, and per-tenant concurrency tracking that respects each customer's available capacity. A naive implementation that fires 10 parallel requests per tenant will intermittently fail with CONCURRENCY_LIMIT_EXCEEDED errors, and those failures cascade because retries consume more concurrency.
Token-Based Authentication (TBA) in NetSuite is account-specific. Each customer's NetSuite instance has a unique account ID (formatted like TSTDRV1234567 for sandbox or 1234567 for production), and API endpoints are realm-specific: https://{accountId}.suitetalk.api.netsuite.com. This means your authentication layer needs to manage per-tenant credentials with per-tenant endpoint routing. Unlike Salesforce, where all instances share a common login endpoint that redirects to the correct instance, NetSuite requires you to know the account ID before you can authenticate. Token refresh works differently too: NetSuite tokens don't expire on a time basis the way OAuth refresh tokens do, but they can be revoked by administrators, and your integration needs to detect revocation and surface it to the customer without breaking other tenants.
Custom records and custom fields are universal in NetSuite deployments. Nearly every mid-market and enterprise NetSuite customer has custom record types (created via SuiteScript or point-and-click customization) that represent business concepts specific to their operations. A manufacturing company might have custom records for production orders. A SaaS company might have custom records for subscription management. Your integration needs to discover these at onboarding time, map them to your application's data model, and handle schema changes when the customer modifies their customizations. The standard NetSuite record types (Customer, Invoice, SalesOrder, Vendor) are just the starting point. The real data lives in custom records, and every tenant's custom records are different.
Sandbox-to-production drift is the final NetSuite trap. Customers test integrations in sandbox accounts, which are periodic snapshots of production. But sandboxes drift: customizations made in production after the sandbox refresh don't appear in sandbox. Fields that exist in production are missing in sandbox. Saved searches that work in production return different results in sandbox because the data is stale. Teams that validate their integration in sandbox and deploy to production discover discrepancies that require per-tenant debugging.
SAP Business One: Session Management, Company Databases, and Approval Workflows
SAP Business One is architecturally different from NetSuite in ways that break assumptions at every layer. Where NetSuite uses token-based authentication with long-lived credentials, SAP B1 uses session-based authentication with aggressive timeouts. Where NetSuite's multi-tenancy is account-based with shared infrastructure, SAP B1's multi-tenancy is database-based with isolated data stores.
The Service Layer is SAP B1's REST API, and it's genuinely well-designed for single-tenant use. It exposes business objects as REST resources, supports OData-style queries, and returns clean JSON. The problem is session management. Every API interaction requires an active session, and sessions expire after 30 minutes of inactivity. This means your integration needs to maintain, monitor, and refresh sessions per tenant. At 200 tenants, you're managing 200 independent sessions, each with its own timeout clock. If a session expires mid-operation (say, during a paginated read of 10,000 invoices), the operation fails and you need to re-authenticate, re-establish the session, and resume from where you left off. Most retry logic doesn't account for session expiration as a distinct failure mode from network errors or rate limits.
The company database architecture is SAP B1's most significant multi-tenancy challenge. Each SAP B1 tenant operates against a separate SQL Server database. The Service Layer connects to a specific company database at login time. This means your integration can't share a single authenticated session across tenants; each tenant requires its own session pointing at its own database. It also means that schema differences between tenants aren't just configuration differences (like custom fields in NetSuite). They can be structural database differences: different User Defined Fields (UDFs) create actual columns in the underlying SQL tables. A UDF named "U_CustomRegion" in one tenant's database doesn't exist in another's. Your data model needs to accommodate this per-tenant schema variation at the database level, not just the API level.
User Defined Fields in SAP B1 are added through the administration interface and become first-class citizens in the API response. They appear as properties on business objects with a "U_" prefix. Unlike Salesforce custom fields (which use a "__c" suffix and are well-documented via the Metadata API), SAP B1 UDFs don't have a dedicated discovery API. You need to query the UserFieldsMD entity to enumerate available UDFs per tenant, and their data types, valid values, and business meaning are entirely customer-specific. An AI financial agent that reads invoices from SAP B1 needs to understand that "U_ProjectCode" in Tenant A maps to a different business concept than "U_ProjCode" in Tenant B, even though both seem to track the same thing.
Approval procedures in SAP B1 can block write operations silently. When a customer configures approval workflows on documents (purchase orders, invoices, delivery notes), API-created documents enter a "pending approval" state instead of being fully committed. Your integration creates a purchase order, receives a 201 Created response, and assumes it's done. But the document is actually waiting for a manager to approve it in the SAP GUI. The API response doesn't clearly distinguish between "created and active" and "created and pending approval." Teams discover this when customers report that records created by the integration are "stuck" or "missing," which usually means they're sitting in an approval queue that the integration didn't know existed.
Batch operations in SAP B1's Service Layer are capped at 100 items per request. For an integration that needs to write thousands of records (bulk invoice creation, mass inventory updates), this means orchestrating hundreds of sequential batch requests with error handling per batch. A single failed item in a batch can fail the entire batch (depending on configuration), so your error handling needs to support partial success, item-level retry, and resumable writes. At multi-tenant scale, with hundreds of tenants each needing bulk operations, the total request volume becomes substantial, and SAP B1's session timeout behavior means you're refreshing sessions throughout long-running bulk operations.
Sage Intacct: XML APIs, Multi-Entity Hierarchies, and Dimensional Accounting
Sage Intacct breaks a different set of assumptions. Where NetSuite and SAP B1 at least offer REST endpoints (NetSuite's REST API, SAP B1's Service Layer), Sage Intacct's API is entirely XML-based. Requests are XML documents posted to a single endpoint. Responses are XML documents. There's no REST resource model, no OData queries, no JSON. Teams accustomed to building integrations against REST APIs need to implement XML serialization/deserialization, handle XML-specific error formats, and parse nested XML response structures that don't map cleanly to the flat JSON objects most application code expects.
Authentication in Sage Intacct uses a session-based model similar to SAP B1 but with its own complications. You authenticate with a sender ID (which identifies your application) and user credentials (which identify the tenant). Sessions have configurable timeouts. The sender ID is issued by Intacct and tied to your integration's registration, which means you need to manage sender-level credentials separately from per-tenant user credentials. At scale, you're managing two credential layers: one for your application's identity and one per tenant.
The multi-entity hierarchy is Sage Intacct's defining architectural feature and its most complex integration challenge. Large organizations using Intacct operate with multiple "entities" (legal entities, business units, departments) organized in a hierarchy. Transactions can occur at any level of this hierarchy. Consolidation reports aggregate data across entities. Your integration needs to understand which entity it's operating in, whether a given transaction is entity-specific or consolidated, and how inter-entity transactions (like intercompany transfers) appear in the API.
This hierarchy interacts with permissions in non-obvious ways. A user credential might have access to Entity A but not Entity B. An API call that succeeds for one entity might return a permission error for another, even within the same tenant. Your multi-tenant integration needs entity-aware credential management: knowing not just which tenant to authenticate against, but which entity within that tenant, and whether the credentials have sufficient permissions for the operation you're attempting.
Custom dimensions in Sage Intacct extend the standard dimensional accounting model (department, location, project, class) with customer-defined dimensions. These dimensions appear on transactions and can be required or optional depending on the module and the customer's configuration. A customer in construction might have custom dimensions for "Job Site" and "Phase." A customer in healthcare might have dimensions for "Service Line" and "Payer." Your integration needs to discover available dimensions per tenant, determine which are required for which transaction types, and populate them correctly on writes. Missing a required dimension on a write operation causes a hard failure, and the error message from the API ("Required field missing: CUSTOMDIM1") doesn't tell you what the dimension represents in business terms.
Rate limits in Sage Intacct are tied to the customer's contract tier, not to a universal platform limit. This means different tenants have different rate ceilings, and your integration needs to track and respect per-tenant limits. A customer on a standard contract might allow 5 requests per second. A customer on a premium contract might allow 20. If your integration treats all tenants equally, it will either under-utilize premium tenants or throttle standard ones. Neither is acceptable at scale.
Required fields in Sage Intacct vary by module in ways that aren't documented in a single schema endpoint. The fields required to create an AP Bill are different from those required to create an AR Invoice, and both differ from a GL Journal Entry. Custom configurations add more required fields on top of the base requirements. The only reliable way to determine required fields for a given tenant and transaction type is to attempt a write and parse the validation errors, which is an unacceptable integration pattern for production systems. Teams building native integrations with Sage need per-tenant, per-module field requirement maps that stay current as customers modify their configurations.
Why This Breaks Every Generic Integration Approach
The technical diversity across these three ERPs is the core reason why generic integration approaches fail for multi-tenant ERP connectivity.
Unified APIs like Merge and Apideck attempt to normalize ERP data into a common schema. This works for CRMs, where the data models are roughly similar (contacts, deals, companies). It does not work for ERPs, where the data models diverge fundamentally. NetSuite's custom records have no equivalent in SAP B1's UDF-extended business objects, and neither maps cleanly to Sage Intacct's dimensional transaction model. A unified API that flattens these differences into a common schema loses the specificity that makes ERP data useful. An AI financial agent that can't access NetSuite saved searches, SAP B1 approval statuses, or Sage Intacct custom dimensions is missing exactly the data it needs to make intelligent decisions.
Embedded iPaaS platforms like Paragon and Prismatic offer pre-built ERP connectors, but they share a common limitation: polling-based architectures that don't handle ERP-specific concurrency and session management well. Paragon's shared infrastructure means your NetSuite integration competes for API quota with other Paragon customers hitting the same NetSuite accounts. Prismatic's tenant-based pricing means your bill scales with customer count rather than actual ERP data volume, which punishes growth. Neither provides the per-tenant credential ownership, per-tenant field mapping, and per-ERP session management that multi-tenant ERP integration requires.
Building in-house is what most teams attempt first, and it's what most teams regret. The first ERP integration (usually NetSuite, because that's what mid-market customers use) takes 3-4 months. The second (SAP, because an enterprise customer demands it) takes another 4-5 months because the architecture is completely different. The third (Sage, because a vertical you're expanding into runs on it) takes another 3-4 months. You've now spent a year building ERP integrations, and you haven't even addressed multi-tenancy at scale, per-tenant field mapping, managed credential refresh, or bulk write optimization. This is the integration debt trap at its most expensive: each ERP is a separate engineering project, and maintaining three separate integration codebases is a permanent drag on your team.
Comparison: Multi-Tenant ERP Integration Approaches
| Dimension | DIY (In-House) | Unified API (Merge, Apideck) | Embedded iPaaS (Paragon, Prismatic) | Integration Infrastructure (Ampersand) |
|---|---|---|---|---|
| NetSuite SOAP + REST | Full control, full maintenance burden | Normalized to common schema, loses saved searches and custom records | Pre-built connector, shared infrastructure, polling-based | Native connector with SuiteTalk + REST support, per-tenant concurrency management |
| SAP B1 session management | Must build session pooling, timeout handling, per-tenant database routing | Abstracted away, limited to supported operations | Basic connector, limited session lifecycle management | Managed session pooling with automatic refresh, per-tenant database routing |
| Sage Intacct XML API | Must build XML serialization, entity hierarchy support, dimensional mapping | Partially supported, custom dimensions often lost | Basic connector, limited multi-entity support | Native XML handling with entity-aware operations and custom dimension discovery |
| Per-tenant field mapping | Must build custom mapping layer per ERP | Not supported (common schema only) | Enterprise tier only (Paragon) | Included on every tier, per-tenant, per-ERP |
| Credential ownership | You own everything | Vendor holds tokens | Vendor holds tokens | You own and can export credentials |
| Bulk write optimization | Must build per-ERP (100-item batches for SAP, different for NetSuite/Sage) | Limited, often single-record only | Varies by connector quality | Managed bulk writes with per-ERP optimization and error handling |
| Time to production | 3-5 months per ERP | 2-4 weeks (limited depth) | 4-8 weeks | 2-4 weeks (full depth) |
| Ongoing maintenance | 1-2 engineers per ERP, permanently | Vendor-managed, but limited to common schema | Vendor-managed within connector capabilities | Managed infrastructure, YAML-based config updates |
How Integration Infrastructure Solves the Multi-Tenant ERP Problem
The pattern that works for multi-tenant ERP integration is integration infrastructure: a platform layer that handles per-ERP API complexity, per-tenant configuration management, and managed authentication, while giving your application full control over the data model and business logic.
Ampersand's approach to ERP integration addresses each of the challenges described above through a combination of native connectors, declarative configuration, and managed infrastructure.
For NetSuite, Ampersand's connector supports both SuiteTalk SOAP and REST API operations, including saved search execution, custom record reads/writes, and SuiteQL queries. Per-tenant concurrency management tracks each customer's available request capacity and queues operations accordingly, preventing CONCURRENCY_LIMIT_EXCEEDED errors without requiring your application to implement concurrency logic. Token-Based Authentication is managed with automatic detection of token revocation and customer-facing re-authentication flows.
For SAP Business One, Ampersand manages session lifecycle per tenant, including automatic session refresh before the 30-minute timeout, transparent re-authentication on session expiration during long-running operations, and per-tenant company database routing. UDF discovery maps available User Defined Fields per tenant at onboarding, and schema change detection surfaces new or modified UDFs as they appear. Approval procedure awareness tracks document states post-creation, so your application knows whether a document is active or pending approval.
For Sage Intacct, Ampersand handles XML serialization/deserialization transparently, exposing a clean data interface to your application regardless of the underlying API format. Multi-entity support routes operations to the correct entity within a tenant's hierarchy, with permission-aware credential management that tracks entity-level access. Custom dimension discovery maps available dimensions per tenant and per module, including required/optional status, so write operations include all necessary dimensional data.
Across all three ERPs, per-customer field mapping lets your application's data model adapt to each tenant's specific configuration. Your AI financial agent defines what data it needs (invoice amount, vendor name, project code, approval status), and Ampersand's field mapping layer resolves those logical names to the actual fields in each customer's ERP instance. When a customer adds a UDF, modifies a custom dimension, or renames a field, the mapping updates without code changes. This is the difference between native integrations that work for real customers and demo integrations that work for standard configurations.
The declarative, YAML-based configuration model means your ERP integrations are version-controlled, reviewable, and deployable through your existing CI/CD pipeline. A change to how you handle NetSuite saved searches is a config change, not a code change. This dramatically reduces the maintenance burden of supporting multiple ERPs at scale, which is exactly what engineering leaders mean when they talk about building multi-tenant integrations that don't break at scale.
Managed authentication across all three ERPs handles the credential complexity that teams consistently underestimate. NetSuite's TBA tokens, SAP B1's session credentials, Sage Intacct's sender ID plus user credential model: Ampersand manages all of these with automatic refresh, revocation detection, and customer-facing re-auth flows. As we've covered extensively, auth and token management alone is not an integration, but it's a necessary foundation, and getting it wrong at the ERP layer (where session timeouts and credential structures vary by vendor) is a common source of production incidents.
As Hatch CTO John Pena put it: "Ampersand lets our team focus on building product instead of maintaining integrations. We went from months of maintenance headaches to just not thinking about it." For teams maintaining three separate ERP integration codebases, the maintenance headache is tripled, and the case for managed infrastructure is proportionally stronger.
FAQ: Multi-Tenant ERP Integration for AI Products
Q: We only need NetSuite today. Should we still think about multi-ERP architecture?
A: Yes, emphatically. The customers who need NetSuite today are mid-market. The enterprise customers you'll sell to in 12-18 months run SAP. The vertical you'll expand into next year runs Sage. If your NetSuite integration is built on assumptions that don't translate to SAP or Sage (like REST-only API access, or OAuth-style token management), you'll need to rearchitect when you add the second ERP. Building on integration infrastructure from day one means your second and third ERP integrations are configuration additions, not engineering projects.
Q: Can unified APIs handle ERP integration adequately?
A: For basic read operations against standard objects, unified APIs provide a quick path to initial integration. The problem is depth. Unified APIs normalize ERP data into common schemas, which strips out the ERP-specific data (NetSuite saved searches, SAP approval statuses, Sage custom dimensions) that makes ERP integration valuable. If your AI product only needs to read invoices and customers, a unified API might work. If it needs to understand the full financial picture, including custom fields, approval workflows, and dimensional accounting, you need native integrations that preserve ERP-specific data fidelity.
Q: How do we handle per-tenant schema discovery across multiple ERPs?
A: Each ERP has a different schema discovery mechanism. NetSuite exposes custom records and fields through the Customization API and SuiteScript metadata. SAP B1 exposes UDFs through the UserFieldsMD entity. Sage Intacct requires querying dimension definitions and field metadata per module. Your integration layer needs ERP-specific schema discovery logic per tenant, refreshed periodically to catch configuration changes. Ampersand handles this automatically, surfacing per-tenant schema information that your application can use for dynamic field mapping without building three separate discovery implementations.
Q: What's the biggest risk with multi-tenant ERP integrations?
A: Credential and session management failures that cascade across tenants. A bug in your SAP session refresh logic that causes sessions to expire mid-operation can affect every SAP tenant simultaneously. A NetSuite token revocation that your integration doesn't detect can silently break data sync for that customer while other tenants work fine. The risk isn't that one tenant breaks; it's that the failure mode is different per ERP, per tenant, and per operation type. Managed infrastructure that handles credential lifecycle per ERP and per tenant isolates these failures and surfaces them to the right customer without affecting others.
Q: How long does it take to add a new ERP to an existing multi-tenant integration?
A: With in-house code, 3-5 months per ERP is typical, assuming your team has relevant ERP experience. Without experience, add 2-3 months for learning the API surface. With integration infrastructure like Ampersand, adding a new ERP is typically a 2-4 week effort focused on configuration (defining which objects to sync, mapping fields, setting up auth flows) rather than engineering (building API clients, session management, error handling). The infrastructure handles the per-ERP complexity; your team handles the business logic.
Q: Our AI product needs real-time ERP data. Is that possible across all three ERPs?
A: It depends on the ERP. NetSuite supports real-time events through SuiteScript User Event scripts and RESTlets that can push data on record changes. SAP B1 doesn't have native webhooks, but the Service Layer supports polling with change tracking. Sage Intacct similarly relies on polling with timestamp-based change detection. Ampersand's Subscribe Actions normalize these different real-time and near-real-time mechanisms into a consistent event model, so your AI product receives data change events in a uniform format regardless of whether the underlying ERP pushes or requires polling. For teams building AI products like 11x, whose AI phone agent cut response time from 60 seconds to 5 using Ampersand's Subscribe Actions, this normalization layer is the difference between real-time intelligence and stale data.
Conclusion: ERP Integration Is a Platform Problem, Not a Project
The fundamental mistake teams make with multi-tenant ERP integration is treating it as a series of projects: build the NetSuite integration, then build the SAP integration, then build the Sage integration. Each project succeeds on its own terms, but the collective maintenance burden grows linearly, and the architectural inconsistencies between ERPs create bugs that are expensive to diagnose and fix.
ERP integration at multi-tenant scale is a platform problem. It requires infrastructure that abstracts per-ERP complexity (session management, authentication patterns, API formats, rate limit models) while preserving per-ERP depth (custom records, UDFs, dimensional accounting, approval workflows). It requires per-tenant configuration management that handles the reality that every customer's ERP instance is unique. And it requires managed credential lifecycle that prevents the session expiration, token revocation, and permission errors that cause silent data failures in production.
This is what integration infrastructure is built for. Not abstracting ERPs into a lowest-common-denominator schema, but providing the platform layer that handles the hard operational problems (auth, sessions, concurrency, bulk writes, schema discovery) so your engineering team can focus on the business logic that makes your AI product valuable.
If your team is building or planning multi-tenant ERP integrations and wants to understand how Ampersand handles the per-ERP complexity described in this post, you can explore the documentation, see how the platform works, or speak directly with an engineer who can walk through your specific ERP requirements. The connectors are native. The field mapping is per-tenant. And your customers' credentials stay yours.