Skip to content

ERPNext glossary

Server Script

Also called Server Scripts, Frappe server script, ERPNext server script

Definition

A Server Script is a Frappe record holding Python code that is written in the desk and run in a restricted sandbox. Depending on its type, it runs on a document event, answers an API call, runs on a schedule or filters list queries, all without a custom app or a deploy.

Open the Server Script list in ERPNext, click New, and you get a code editor and a Script Type dropdown. Whatever you type there is stored in the database and executed by Frappe through safe_exec, which uses RestrictedPython to limit what the code can reach.

The four types (five on v16)

DocType Event attaches to one DocType and one lifecycle event. The list covers Before Validate, Before Save and After Save, Before and After versions of Insert, Submit, Cancel, Delete and Rename, the two “Submitted Document” save events for fields allowed on submit, and Before Print. You get the document as doc. The labels don’t match controller method names one to one (Before Save runs at validate), which matters when you move a script into code later.

API turns the script into an endpoint under /api/method/<method name>. You can tick Allow Guest for unauthenticated calls and Enable Rate Limit to throttle them, and you return data by setting frappe.response["message"].

Scheduler Event runs on a frequency: Hourly, Daily, Weekly, Monthly, Yearly, the “Long” queue variants, or a Cron expression.

Permission Query adds a condition to list queries for a DocType. Frappe hands you user and reads back whatever you assign to conditions.

Frappe v16 adds a fifth type, Workflow Task.

Turning them on

On a self-hosted bench, Server Scripts are off until you set server_script_enabled. In the v15 and v16 source, Frappe reads that key only from common_site_config.json, so set it bench-wide:

bench set-config -g server_script_enabled 1

If it’s missing, running a script fails with a “Server Scripts Disabled” error. On Frappe Cloud, the docs say you need a private bench; shared public benches don’t allow them.

An example: sales reps see their own orders

# Script Type: Permission Query
# Reference Document Type: Sales Order
is_manager = frappe.db.exists(
    "Has Role", {"parent": user, "parenttype": "User", "role": "Sales Manager"}
)
if user != "Administrator" and not is_manager:
    conditions = "`tabSales Order`.owner = " + frappe.db.escape(user)

Keep in mind what this does and doesn’t cover. Permission query conditions filter results from frappe.db.get_list, which drives list views and reports. They don’t decide whether someone can open a single document by its URL. That’s the job of role permissions, or a has_permission hook in an app’s hooks.py.

Limits to know before you rely on one

The sandbox has no import, and frappe.db.sql accepts read queries only. Names starting with an underscore are blocked. DocType Event scripts are also skipped while bench migrate or an app install is running, so a rule enforced by a script won’t fire on records a patch creates.

There’s no test runner and no Git history either. For a two-line validation that’s a fair trade. For anything posting to the ledger, it isn’t, and we’ve set out where we draw that line in Server Scripts vs a custom app. erpfly doesn’t generate Server Scripts for business rules. It writes the same logic as Python in a Frappe app, wired through doc_events, so it ships with tests in the pull request.

Where Server Script comes up

Terms used on this page

More ERPNext terms

Your next module is one paragraph away

Write it the way you’d explain it to a new hire. We’ll turn it into an app you can read, test and install.

ERPNext v16

Create your account

Free to start. No card needed.

By signing up you agree to our terms and privacy policy.