When ERPNext server scripts are the right tool
A team that needs a small piece of server-side logic in ERPNext, such as rejecting a Sales Invoice that breaks a internal rule on save or exposing an endpoint an external system can call, can often use a Server Script. Server Scripts run Python on document events or as API endpoints without a custom app. Since version 15 they are disabled by default and must be turned on with the server_script_enabled setting, so the first check is whether your environment allows them at all. Frappe Cloud public shared benches do not allow them, which rules out that hosting option for this approach. Confirm the setting and hosting model with your platform owner before you design anything around a script.
A small change is one that reads or validates a few fields of a single DocType, sets a value from related records, or returns a short response to a request. Logic that touches many DocTypes, needs its own DocTypes, ships reusable pages or reports, or must be versioned, tested and deployed with the same discipline as other code usually belongs in a custom app. ERPNext is licensed under GPL-3.0 and is built on the Frappe Framework, which is MIT-licensed, so both the platform and any app you build on it carry licensing obligations your legal team should review. Ask who will maintain the script after go-live, and whether it will survive a major version upgrade.
Server Scripts run inside a restricted execution environment, so they cannot import arbitrary Python modules or reach the file system the way an app can. Treat that restriction as a design limit, not a bug to work around. Before you commit, write down the trigger, the expected inputs and outputs, the roles that may call an API endpoint, and the test you will run on a staging site. If the answer to any of these needs more than a short script, plan a custom app from the start, as described in the sibling topic on building custom Frappe apps for ERPNext.
Where server scripts fit: document events, API endpoints and scheduled logic
Document event scripts run Python when a document is validated, saved, submitted or cancelled, so they suit rules that must hold no matter how a record was created, whether from the desk, the Data Import tool, an API call or another script. Typical uses include checking a field combination before a sales order is submitted, or setting a value on a related record after a save. The usual trouble is side effects: a script that writes to other documents can trigger their own events, and an error raised in a before-submit event blocks the user with a message that may be unclear to them.
- Event choice: Pick the event deliberately. Validation rules belong before save or validate, while follow-on actions belong after submit, because cancellation and amendment paths also fire events.
- Bulk imports: Confirm how the logic behaves when many records arrive through the Data Import tool, since the documentation advises importing only a few thousand records at a time.
- Cascading writes: Map which other DocTypes a script touches and what their events do, so one save does not start a chain nobody planned.
- Error messages: Test the messages users see when validation fails, including in partially filled forms.
- Ownership: Record which script owns each rule so that two scripts do not change the same field.
Before release, test each event path on a staging copy with realistic document volumes and the roles that will use it.
Server scripts can also be exposed as API endpoints, which lets other systems call a defined function over HTTP with the permissions of an authenticated user. This suits small, well-bounded integrations, such as returning a computed status to a warehouse tool or accepting a simple payload from a form on another system. The trouble starts when an endpoint grows into a general integration layer, because its input validation, error handling and authentication then carry more weight than a script in the desk is built to handle.
- Authentication: Confirm which authentication method the calling system uses and which ERPNext user the request runs as, since that user's roles limit what the endpoint can read and write.
- Input validation: Validate every field of the incoming payload, because the caller is not a person using the desk.
- Error responses: Define the status and message returned for bad input, so the calling team can handle failures.
- Rate and volume: Ask how often the caller will invoke the endpoint and whether the server can absorb that load alongside normal users.
- Versioning: Plan how a changed endpoint is rolled out without breaking the caller.
Confirm these points with the team that owns the calling system before the endpoint is written, not after.
Client scripts run in the browser when a form loads or a field changes, and they are the right place for behaviour that only affects what the user sees, such as filtering a link field or showing a button. Server scripts run on the server and are the right place for rules that must hold for every user and every entry path. Teams often put a rule in a client script because it is quick to write, and then discover that an API call or import bypasses it entirely.
- Enforcement: Keep any rule that protects data integrity on the server, because a browser-side check can be skipped by a direct API call.
- Display logic: Use client scripts for filters, read-only toggles and helper buttons that improve the user experience.
- Duplicate logic: Avoid placing the same rule in both places, since the two copies drift apart over time.
- Browser testing: Test client behaviour across the browsers your users run, because client scripts depend on the page state of each session.
- Documentation: Note in one place which script type enforces each rule, so auditors and new administrators can follow the control.
Review each existing rule and ask whether a bypass would cause a financial or data error. If it would, the rule belongs on the server.
Configuration options and when each fits
This table compares the five places a team can put ERPNext behaviour: server scripts on document events, server scripts as API endpoints, client scripts, notifications and automation flows, and a custom app with hooks. Read each row to see where the logic executes, who can change it and how it is versioned, then match the option to the kind of change you need.
| Option | Where the logic runs | Who can change it and how it is versioned | When it fits |
|---|---|---|---|
| Server Script (document event) | Python on the server when a document event fires, such as validate or on_submit | System Manager users in the desk. Lives in the database, so it moves between sites only by export or manual copy | Short validation or field-derivation rules tied to one DocType, where the rule is small and owned by the admin team |
| Server Script (API) | Python on the server when an API endpoint is called | System Manager users in the desk. Stored in the database and changed in place, with no built-in code review step | A small endpoint that an approved integration calls, with a narrow input and output |
| Client Script | Browser, inside the form the user is working in | Users with the right role in the desk. Stored in the database like server scripts | Form behaviour such as field filters, default values and custom buttons that do not need server data |
| Notifications and automation flows | Server-side rules evaluated by the platform, with no custom code for simple cases | Configured in the desk by authorized users. Stored as configuration records | Routine alerts, assignments and multi-step flows that the built-in actions can express |
| Custom app with hooks | Python code in a Frappe app, loaded by the bench at runtime and on migrate | Developers in your repository, reviewed and deployed through your normal release process | Logic that is large, shared across sites, needs tests, or must survive upgrades under version control |
| Enabling requirement | Server Scripts are disabled by default from version 15 and need server_script_enabled | Enabled by an administrator on the self-hosted bench, which the Frappe docs describe as a setting you control | Confirm before design starts, since Frappe Cloud public shared benches do not allow server scripts |
How to enable server scripts and set them up safely
This sequence takes a team from confirming its ERPNext and Frappe versions to promoting a tested server script through staging, with a check at each stage so the script runs only where it was intended.
Confirm the ERPNext and Frappe versions
The platform owner checks which ERPNext and Frappe Framework versions the site runs, using the bench version command on a self-hosted bench or the dashboard on Frappe Cloud. Confirm the version is still listed as supported on Frappe's supported versions page, since version 14 reached end of life on January 31, 2026, and that version 15 and version 16 are the lines in scope. Server Scripts are disabled by default from version 15 onward, so the version determines which setting is needed. Done when the version numbers are written into the change record and the team agrees they are supported.
Confirm the hosting type allows server scripts
Before changing anything, the platform owner confirms where the site is hosted. Frappe documentation states that Frappe Cloud public shared benches do not allow server scripts, so a site on that tier cannot use this feature without moving to a plan or host that permits it. Ask the hosting provider in writing whether custom server-side Python is permitted on the bench. On a self-hosted bench, you control this setting directly. Done when the hosting type is recorded and the feasibility question is answered before the next step.
Enable server_script_enabled in site configuration
On a self-hosted bench, the administrator sets the server_script_enabled key in the site configuration file, usually site_config.json for the target site, then restarts the bench processes so the change loads. Enable it only on the sites that need it, not globally, and record who approved the change. Check that the Server Script DocType now appears in the awesome bar or the desk search. Done when the setting is in version control or the change log and a test user can open a new Server Script form without an error.
Create the script on a non-production test site
A developer creates the first script on a separate test site with copied, non-sensitive data. Choose the script type (Document Event or API), the reference DocType, and the event such as Before Save or Validate, and keep the logic small enough to read in one sitting. Use the restricted Python environment and avoid assumptions that imports or file access will work. Save the script as disabled first, then enable it. Done when the script saves without validation errors and the developer has a written description of what it changes.
Verify the trigger and the permissions
Test the script with at least two user roles: one that should be allowed to trigger the logic and one that should not. Create, update and submit records on the test site, and confirm that the script fires on the expected event and only then. For API scripts, call the endpoint with a valid token and a token without rights, and check the responses. Review the error log and the activity on the affected documents. Done when the expected changes appear, unauthorized calls are refused, and no unintended records are altered.
Test failure paths and the limits of the script
Deliberately pass bad input, empty fields and large batches to the script, and confirm it raises clear errors instead of corrupting data. Note any operation the restricted environment blocks, such as an import the script needs, and decide whether that logic belongs in a custom app instead. Record the result and the reason for any rejected design. Done when every failure produces a readable message and the team has a written decision on whether the script stays a server script or moves into an app.
Promote the script through staging to production
Export the script configuration with the site's customization workflow, apply it to a staging site that mirrors production settings, and repeat the trigger and permission tests there. Confirm that server_script_enabled is set on staging and production in the same way, and that the script is attached to the correct site rather than copied across all of them. Schedule the production change with a named owner and a rollback step that disables the script. Done when staging passes the same checks as the test site and production shows the script active with the expected behavior.
Monitor the script after go-live
After production release, the operations owner reviews error logs, the scheduler and background job queue, and user reports for the first working cycle, then keeps the script under the same change control as other customizations. Document who may edit server scripts, since changes made in the UI bypass code review unless the team enforces a process. Done when the monitoring owner has signed off, the review cadence is recorded, and the team has a plan to move the logic into a custom app if it grows past the limits found in testing.
What the restricted environment blocks and why it matters
Server scripts do not run as ordinary Python. They execute inside a restricted environment that exposes a limited set of modules and built-ins, so code that works in a local script or a custom app may fail the moment it is pasted into a Server Script record. The exact allowed set changes between Frappe versions, so do not plan from memory or from a forum answer. Check the current Server Script page in the Frappe Framework documentation for the version you run, then test each import and call your logic depends on in a staging site with the same version and settings as production.
The practical consequence is a sorting rule. Logic that needs an external Python library, direct file system access, network calls to systems outside a simple HTTP request, or work that runs for a long time usually belongs in a custom app or a background job instead. A script that waits on a slow third-party API inside a document event holds up the user's save, and a script that depends on a library not in the allowed set will fail at runtime rather than at deployment. Ask your partner or internal team to tag each planned script with what it imports, what it touches outside the database and how long it may run, then move anything that fails those checks before build starts.
Restriction also shapes governance. Because server scripts are enabled through a site setting and run code from the database, a change to a script can alter business behavior without a code review. Decide who may create or edit Server Script records, record each change in your change process, and keep the scripts you rely on in version control through an export, so an upgrade or a restore does not silently remove them.
Security and operational risks of enabling server scripts
Enabling server scripts moves Python execution into the ERPNext database-backed application, so the risks sit in who can write code, what runs on every save, and what breaks at upgrade. Each card names the control to put in place before the setting is switched on in a production site.
Script creation is a privileged permission
Anyone who can create or edit a Server Script record can run Python against your data with the permissions of the application process. Restrict the Script Manager role to a small, named group, review every script in a change process, and check the role assignments on each site after upgrades or user onboarding. Treat script authors as administrators of the data they touch.
Auditing scripts through version history
A script edited in the Desk can change behavior without a code review, a commit or a deployment record. Ask who has edited each script, when, and what changed, and keep the authoritative copy in version control. Confirm with your team how your site records these changes and whether the export process captures the scripts you rely on.
Submit and cancel behavior
Document events such as before submit, on submit and on cancel run on accounting and stock documents where mistakes are expensive to reverse. A script that alters totals, posts extra entries or fails midway can leave documents in an inconsistent state. Test each event against submit, amend and cancel paths using a copy of production data before enabling it.
API endpoints and guest or portal access
A Server Script API endpoint can return data to whoever is allowed to call it, and the allow guest option removes the login requirement. Review every endpoint for the roles it can reach, the fields it returns, and the input it trusts. Keep guest access off unless a documented use case needs it, and test endpoints as a portal user and as an anonymous caller.
Performance impact on saves
Document event scripts run inside the save path, so a slow query or external call delays every user who saves that DocType. Scripts that loop over large child tables or call remote services are the usual cause. Measure save times before and after each script, keep logic light, and move heavy work to background jobs. Confirm the scheduler and worker capacity planned for your site.
Upgrade behavior and version drift
Scripts reference DocType fields, method names and standard behavior that can change between ERPNext releases. A script that works on version 15 may fail silently or block saves after an upgrade. Keep an inventory of every script with its purpose and owner, and add script tests to the upgrade checklist. Check the release notes for each target version before you schedule the upgrade.
When to move logic from server scripts into a custom app
Move logic out of server scripts and into a custom Frappe app when the code needs the controls a script cannot give you. The clearest signals are a need for version control and code review, dependencies that must be pinned, automated tests, and the same logic running on more than one site. A server script lives in the database, so it is changed through the Desk, is not reviewed in a pull request by default, and is hard to test in isolation. If your change control requires every line of business logic to be reviewed, tagged and reproducible, a script is the wrong home for it. Also check whether the logic calls external Python libraries or services that need installing and versioning, because the restricted script environment limits what you can import and run.
Other signals appear as the logic grows. Complex overrides of standard ERPNext behavior, such as changing how stock valuation or invoice submission works across many doctypes, usually need hooks, overridden controllers or custom doctypes that a server script cannot register. Heavy processing is another signal: long-running jobs, bulk record handling and anything that should run through background workers and the scheduler belong in code that your team can monitor, retry and profile. Before you commit, confirm the exact script limits for your version and whether your hosting plan allows enabling server_script_enabled. Ask your implementation partner which of these signals applies, and record the answer, because the choice shapes your upgrade effort. Scripts usually survive upgrades with less work, while an app requires its own compatibility checks at each major version. The app route is covered on the sibling topic on building a custom Frappe app for ERPNext.
Questions teams ask before enabling or building server scripts
Can our own team build and enable ERPNext server scripts without a partner?
Yes, a team with Frappe experience can usually write and enable server scripts on a site it controls, since the feature is part of the framework and documented by Frappe. The first step is confirming that the hosting setup allows it: according to the Frappe documentation, server scripts are disabled by default from version 15 and must be turned on with the server_script_enabled setting, and Frappe Cloud public shared benches do not allow them. Your team should also own the review step, because a script runs with the permissions of the code path that triggers it and can change financial or stock records. A partner is useful when the team lacks that Frappe depth or needs an independent review before go-live.
What makes a server script project larger or more expensive?
The main drivers are the number of scripts, how many DocTypes and document events they touch, and how much testing the business expects. Each script that changes accounting, stock valuation or production records needs test data, role-based checks and a rollback plan. Permissions add effort because API endpoints created by scripts must be reviewed for who can call them and what they return. Hosting matters too: a site on a managed platform that blocks server scripts pushes the work toward a self-hosted bench or a custom app. Ask any quote to list each script, its trigger, the records it changes and the test cases it needs.
Do server scripts survive ERPNext upgrades?
Server scripts are stored as records in the database rather than in your code repository, so they are not automatically versioned in git the way app code is. They generally carry across a standard upgrade of the same major version, but each one can break when a DocType field, method or event changes in a new release. Before any upgrade, test every script against the target version in a staging site, and check the release notes for the ERPNext and Frappe versions you run. Teams that need scripts under version control, code review and automated tests usually move them into a custom app.
When is a partner worth bringing in for server script work?
A partner is worth considering when scripts start to hold business rules that several departments depend on, when they affect posting or compliance records, or when the internal team has no one who can review Python against the Frappe API. It is also sensible when the same logic should move from scripts into a versioned app with tests. A fixed-scope engagement gives you a written list of scripts, deliverables and acceptance checks before work starts. Ask the partner how they will test changes, how they will handle upgrades and who will support the scripts afterward.
What does the internal team need to provide for this work?
You need a named business owner for each process the scripts touch, such as invoicing, purchasing or stock movement, who can confirm expected behavior and sign off on tests. You also need access to a staging site that mirrors production settings, sample data that reflects real transactions without exposing personal data, and a person who can grant the right roles to test users. Your engineers should know where the site's logs and scheduler live, because script errors often appear there first. Confirm these items in the scope so that delays on access or approvals do not appear as overruns later.
How long does a typical server script change take?
The duration depends on how many records and roles a change touches and how many test cases it needs, so a single validation rule and a multi-step script that posts entries are very different jobs. A small change on a staging site with clear requirements is usually quick, while a set of scripts that affects accounting and needs review, testing and an upgrade check takes considerably longer. The best way to get an accurate timeline is a written scope listing each script, its trigger and its acceptance tests. Ask for the timeline to follow from that list rather than from a general estimate.
Which security risks should we check before enabling server scripts?
Server scripts run Python inside the ERPNext site, so anyone who can create or edit them has a powerful way to change data, so enabling them should be treated as a privileged change. Restrict who holds the role that can author scripts, review every script that exposes an API endpoint and confirm that it checks permissions and validates input. Keep scripts that touch payments, bank details or personal data under the same review as other code changes, and log who changed each script. Your security team should also confirm that the restricted execution environment fits the logic you need before committing to scripts instead of an app.
How DeployOpen works on this
DeployOpen starts by deciding whether a Python server script is the right tool for each piece of logic, or whether it belongs in a custom app. We then enable and build only what the written scope covers, and hand over the configuration, tests and runbook your team needs to run it. The work stays inside your own ERPNext infrastructure.
How we scope
- We list each business rule, document event and API endpoint you want to automate, and mark which ones fit a server script.
- We check the target environment, including whether it is self-hosted and whether server_script_enabled can be set there.
- We flag logic that needs file-level code, external libraries or heavier testing, and propose a custom app for it in the written scope.
How we build
- We enable server scripts through the configured setting, then build each script within the restricted environment and its limits.
- We test each event and API path against sample documents, roles and permission cases before anything reaches production.
- We record each script's purpose, trigger, owner and permissions so later changes can be traced and reviewed.
How we hand over
- You receive the scripts, their configuration and a list of which logic moved into a custom app, with reasons.
- We document how to enable the setting on new sites, how to export and version scripts, and how to test changes.
- We walk your administrators through the runbook so your team can review and maintain the scripts after go-live.
How we support
- Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts.
- Ongoing support, ERPNext upgrade checks and continuous testing are available as a retainer for scripts that keep changing.
- Ask any partner, including us, to confirm how scripts will be re-tested when ERPNext and Frappe versions change.

