VOOZH about

URL: https://www.apideck.com/blog/how-to-build-erp-extensions.md


--- title: "How to Build ERP Extensions: A Developer Guide to NetSuite, SAP, Business Central, Acumatica, and Sage Intacct" description: "A developer guide to building extensions, embedded apps, and UI customizations in NetSuite (SuiteScript), Business Central (AL), SAP (BTP/B1 SDK), Acumatica (C#), and Sage Intacct (Platform Services)." author: "GJ" published: "2026-04-21T00:00+00:00" updated: "2026-04-26T06:32:07.068Z" url: "https://www.apideck.com/blog/how-to-build-erp-extensions" category: "Accounting" tags: ["Accounting", "Guides & Tutorials"] --- # How to Build ERP Extensions: A Developer Guide to NetSuite, SAP, Business Central, Acumatica, and Sage Intacct ### 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](https://www.apideck.com/connectors/netsuite) uses [Token-Based Authentication (TBA)](/blog/integrating-with-the-netsuite-rest-api) 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](https://www.apideck.com/connectors/microsoft-dynamics-365-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. For the full API authentication flows and endpoint patterns, see our [Business Central API integration guide](/blog/microsoft-dynamics-business-central-api-integration-guide-2025). ## Sage Intacct [Sage Intacct](https://www.apideck.com/connectors/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. ![Sage Embedded Platform Services](//images.ctfassets.net/d6o5ai4eeewt/2Or3K16JUteb8T9KfmmRgE/dc11664dc7261487b21ffb0364785476/Screenshot_2026-04-22_at_23.28.13.png) ## 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