NetSuite Customization and ERP Extensions: SuiteScript, Business Central AL, SAP BTP, Acumatica C#, and Sage Intacct Platform Services.
Every ERP vendor has its own answer to the same question: how should third-party developers add functionality without breaking upgrades? The answers vary significantly in tooling and language, but the underlying tension is consistent. Custom code that runs inside the ERP core couples tightly to the ERP's release cycle. Custom code that runs outside connects through APIs and is more stable over time.
This post covers the four platforms where this tradeoff plays out most frequently for Banks, Fintechs and vertical SaaS teams.
NetSuite
NetSuite's extension framework is SuiteCloud. The primary development language is SuiteScript 2.1, which is JavaScript-based. Extensions run inside NetSuite's infrastructure under strict resource limits: script execution time is capped and memory ceilings affect batch processing. A SuiteApp that passes sandbox testing can fail in production under real transaction volume.
SuiteScript provides several script types for different contexts. User Event scripts fire on record load, validate, and save. Client Scripts run in the browser and respond to field changes. Suitelets are full server-side pages hosted on NetSuite's domain, accessible via URL and iFrameable into other views. Portlets render as panels on the NetSuite dashboard.
A concrete example: a revenue operations ISV might build a Suitelet that shows a customer's payment history alongside a real-time credit limit recommendation from an external risk model. The Suitelet fetches transaction data via nlapiSearchRecord, calls the external risk API, and renders an HTML table inside the NetSuite interface. Sales reps access it via a button injected into the Customer record through a Client Script. The whole thing deploys as a SuiteApp through SuiteCloud Development Framework (SDF) and can be listed on the SuiteApp Marketplace.
For API access, NetSuite uses Token-Based Authentication (TBA) for machine-to-machine calls and OAuth 2.0 for user-facing apps. SuiteCloud 2026.1 (February 2026) brought REST web services to functional parity with SOAP, which had previously been required for certain advanced operations. For SuiteApps that expose NetSuite data to external systems, REST-first is now a complete option without the SOAP dependency.
Microsoft Dynamics 365 Business Central
Business Central's extension model runs on AL (Application Language), a proprietary language Microsoft introduced when it moved the platform to the cloud. AL is the only supported path for customizing BC logic. You write it in Visual Studio Code with the AL Language extension, publish apps to a sandbox or production tenant, and distribute through Microsoft's AppSource marketplace for cloud deployments.
AL uses an event-based pattern: the framework publishes events at key points in standard business processes, and your extension subscribes to them. Adding fields to a table requires a table extension object; modifying a page requires a page extension. Business logic lives in codeunits. Subscribing to OnAfterPostSalesOrder to trigger a third-party fulfillment workflow is a common pattern, and it's upgrade-safe because extensions can't modify base objects directly, only layer on top of them.
For UI extensions that go beyond field injection, BC offers control add-ins. A control add-in is declared in AL but renders as an iframe hosting JavaScript and HTML. It communicates back to AL via the Microsoft.Dynamics.NAV.InvokeExtensibilityMethod API. A concrete example: an ISV building a logistics product might embed a live shipment tracking map inside the Sales Order page using a control add-in, pulling carrier API data and rendering it as an interactive element alongside the native BC fields. The iframe boundary keeps the JavaScript isolated; the AL callback handles any state updates back to the record.
Microsoft ships BC updates monthly. One practical consequence of the event subscription model is that if Microsoft removes an event between releases, subscriber code fails silently until you test against the next sandbox. Testing each monthly release in a development tenant before it goes live is not optional for customer-facing BC extensions.
Sage Intacct
Sage Intacct splits its extensibility model into two distinct layers. External integrations use Web Services, which moved from its XML-only API to a REST API in general availability as of Intacct's 2025 Release 1 (February 2025). For extensions that live inside the Intacct interface, the tool is Platform Services.
Platform Services lets developers create custom objects (with their own fields and relationships) and build full applications with menus and workflows. Behavior can be injected via JavaScript using the AJAX SDK. A Platform application groups custom objects and navigation menus into a self-contained mini-app inside Intacct. The entire application definition exports as XML, making it portable across Intacct companies.
A practical example: a professional services firm might use Platform Services to build a project milestone tracker. The developer creates a custom object that links to Intacct's native Project and AP Bill objects. When a milestone is approved, a trigger fires an API call to create a vendor bill. The forms and approval workflow live entirely within Platform Services; the bill creation uses merge fields to pull milestone data into the standard AP Bill form.
The AJAX SDK has a notable constraint: Sage Intacct explicitly warns against manipulating the standard Intacct DOM (hiding fields with jQuery, for example). Any UI behavior built against undocumented internal elements breaks when Intacct updates its interface. The AJAX Gateway is the only supported path for page scripts that need to call Intacct's back end. This is a narrower surface than many developers expect going in.
Sage also announced in November 2025 that third-party AI agents can now be embedded in the Sage Copilot interface, with an MCP server built on top of the Intacct REST API. That opens a different category of extension that doesn't require Platform Services at all and may be the easier path for read-heavy use cases.
Acumatica
Acumatica's customization model gives developers more direct access to the ERP's internals than the other three platforms here. Customizations are written in C# and packaged into Customization Projects, which can be exported and deployed across environments. The Customization Project Editor in the Acumatica UI handles screen modifications alongside the C# code.
The pattern: Acumatica's data objects are declared as DAC (Data Access Class) classes in C#. Business logic lives in Graph classes. An extension subclasses the relevant DAC or Graph, adding or overriding fields and methods without touching the base code. To add an insurance expiration date to the Vendor screen, a developer creates a VendorExtension : PXCacheExtension<Vendor> class for the field, and a VendorMaintExtension : PXGraphExtension<VendorMaint> for validation logic. The Customization Project wraps both for deployment.
For UI, Acumatica shipped a finalized Modern UI in TypeScript and HTML as of 2025 R2 (released September 2025). Extensions to existing Acumatica screens now require creating matching .ts and .html files in the extensions folder under the screen directory. As of 2025 R2, screen personalizations can be included directly in Customization Projects, which lets ISVs guarantee consistent layouts across environments during deployment.
A concrete example from an ISV: a construction SaaS company might add a compliance status panel to the Subcontract screen, pulling insurance and license data from an external API. The Graph extension handles the data fetch on screen load, while the TypeScript file renders the panel in the Modern UI. The Customization Project packages both for deployment to client tenants.
One thing to plan for: as of late 2025, ISVs with multiple Customization Projects targeting the same screen have run into conflicts. Acumatica's export-to-development function overwrites files from the development folder, which affects teams managing several products for the same customer. This isn't a blocker, but it requires explicit project management.
SAP
SAP sells two products under the same brand that have almost nothing in common from an extensibility standpoint: Business One (B1), aimed at SMBs, and S/4HANA, aimed at mid-market and enterprise. The toolchain, deployment model, and API surface differ enough that treating them as the same platform is a common mistake for ISVs trying to cover both.
SAP Business One
B1's extension model uses the SDK, which exposes two APIs for different contexts. The DI API (Data Interface API) is a COM-based interface that runs on the B1 client or application server, giving add-ons full access to business objects. The Service Layer is a REST API that runs on HANA-based B1 deployments (9.1 PL09 and later) and is the recommended path for web-based or multi-threaded integrations that don't require the full depth of the DI API. For new integrations, Service Layer is generally simpler; DI API remains necessary when you need objects the Service Layer doesn't yet expose.
For UI extensions inside the B1 web client specifically, SAP added a UI API extension framework using JavaScript, TypeScript, and SAPUI5. Development happens in VS Code with a dedicated plugin and a Chrome/Edge-based Web Client Inspector for debugging. A UI API extension targets a specific view in the web client (defined by a JSON layout file) and uses a JavaScript controller for logic. A concrete example: an ISV might add a custom panel to the Business Partner Detail view that surfaces a customer's credit insurance status from an external provider. The JSON file positions the new UI element; the controller fetches and displays the data when the panel loads.
B1 does not use SAP BTP in the same way S/4HANA does. BTP integration is possible but not the primary extensibility model for B1 partners. Most B1 ISV add-ons still ship as traditional SDK-based packages installed on the B1 server.
SAP S/4HANA
S/4HANA's extensibility model follows SAP's clean core framework, which separates on-stack from side-by-side extensions. On-stack extensions run within S/4HANA itself, written in ABAP Cloud using only publicly released APIs. Side-by-side extensions run on SAP BTP and communicate with S/4HANA through APIs and events.
SAP formalizes this in a four-level compliance model (A through D). Level A extensions use only released, stable APIs on both paths. Level C and D extensions access internal objects and carry explicit upgrade risk. For ISVs building multi-tenant SaaS on S/4HANA, SAP's August 2025 extensibility documentation positions BTP side-by-side as the preferred architecture: it keeps the extension deployment decoupled from the ERP core, so the ISV manages its own release cadence without waiting on SAP upgrade windows.
For UI work on S/4HANA, the options split by deployment model. Key-user extensibility lets non-developers add custom fields to standard Fiori apps without code. Developer extensibility goes further with SAPUI5 adaptation projects built in SAP Business Application Studio, which overlay changes onto existing Fiori apps without modifying their base code. New standalone apps can be built with SAPUI5/Fiori Elements and hosted either on-stack in the Fiori Launchpad or on BTP.
A concrete example for S/4HANA: a manufacturing ISV might build a quality inspection panel embedded alongside the standard Production Order screen. The panel pulls sensor data from an IoT system via a BTP integration, renders a Fiori-based status indicator in the Production Order screen, and writes inspection results back to S/4HANA through an OData service. The application runs on BTP Cloud Foundry, connects to S/4HANA through a communication arrangement, and is distributed to customers through the SAP Store.
The practical difference between B1 and S/4HANA for an ISV covering both: B1 extensions typically ship as on-premise or hosted SDK packages, while S/4HANA extensions for cloud deployments ship as BTP applications certified through the SAP Store. Different distribution model, different technical stack.
What breaks
Each platform has its characteristic failure mode.
In SAP, the failure mode depends on which product you're targeting. For B1, the DI API's COM dependency limits add-ons to Windows and requires the B1 client to be running, which creates problems in web-only or headless integration scenarios. The Service Layer is cleaner but doesn't cover all DI API objects, so gaps force a context switch back to the older interface mid-project. For S/4HANA, the risk is API scope: if an extension needs access to an internal object not covered by a released API, it falls into Level C or D on the clean core model and carries explicit upgrade risk when SAP ships updates.
In Business Central, the risk is event volatility. Microsoft adds and deprecates AL events as the base application evolves across monthly releases. A subscriber function becomes a runtime error if the event it depends on is removed. This makes continuous testing against the next monthly release non-negotiable for any BC extension in active customer use.
In Sage Intacct, the constraint is scope. Platform Services handles custom objects well, but complex UI injection hits the sandbox limits quickly. Scripts that manipulate the standard DOM break on Intacct's quarterly releases without warning, since those changes are outside the supported API surface.
In Acumatica, the Modern UI transition introduced a new class of migration work. ISVs with ASPX-based screen customizations are rebuilding them in TypeScript for the Modern UI. The new extension file structure requires precision, and multi-project conflicts on shared screens require coordination that didn't exist in the older model.
In NetSuite, governor limits are the consistent friction point. The script execution time cap hits Suitelets that make external API calls with variable latency. Scheduled scripts queue behind other jobs in the account. These limits are fixed and don't scale with a customer's transaction volume.
What "embedded" means at the API layer
The phrase "embedded app" has two meanings that frequently get conflated. In the ERP vendor context, an embedded app is one that runs within the ERP's infrastructure and is distributed through its marketplace. In the SaaS product context, an embedded app is a product experience that lives inside a customer's workflow without requiring them to leave your tool.
Both definitions are valid. They address different problems. The ERP vendor meaning matters when you're building for a single ERP and care about the vendor's distribution channel. The SaaS product meaning matters when you're building across ERPs and want your product to function consistently regardless of which ERP the customer runs.
SAP's August 2025 extensibility documentation describes side-by-side on BTP as the preferred path for ISV partners building multi-tenant SaaS, precisely because it decouples deployment and lets the ISV manage its own release cadence. That matches how most SaaS companies want to operate: own the infrastructure and release schedule, treating the ERP as a data source rather than a runtime environment.
Most extension projects eventually hit a point where the two definitions need to coexist. A vertical SaaS product might embed a lightweight UI extension inside NetSuite to surface its own data in context, while the bulk of data integration runs through a normalized API layer that also handles the customer's other accounting systems. The extension provides presence inside the ERP; the API layer provides breadth. Which of those two problems you need to solve first is the decision worth making explicitly before you pick a toolchain.
Building for multiple ERPs
For internal IT teams configuring a single ERP instance, the per-platform toolchain is the only path. For SaaS companies whose product needs to work across these four ERPs, maintaining native extensions per platform becomes an engineering capacity problem. No two of these platforms share a development language or deployment model.
The practical alternative is to build your product's interface outside those frameworks and use APIs for data exchange. Your product handles its own workflows; the ERP handles ERP-native work. For operations that need normalized access to financial data (invoices, customers, and journal entries) across these platforms, a unified accounting API layer collapses the per-platform API work into a single schema.
Native embedded extensions still have a clear role. When your product genuinely needs to live inside the ERP's UI, building a native extension for that specific ERP is the right call. The decision is about scope: if "embedded" means your UI inside a single ERP, native extensions make sense. If "embedded" means your product functioning consistently regardless of which accounting system the customer uses, a normalized API layer is cheaper to maintain long-term.
Most ISV teams end up with a hybrid: lightweight native extensions for presence inside one or two priority ERPs, and a unified API integration for data sync across the rest.
Ready to get started?
Scale your integration strategy and deliver the integrations your customers need in record time.








