---
title: "Server Scripts"
space: "Frappe Framework"
url: "https://www.prilk.com/docs/frappe-framework/server-scripts"
updated: "2026-05-14"
---

## Server Scripts

A Server Script runs Python on the server when a document event fires. Same purpose as Client Scripts but with full DB access and trusted execution — used for security-critical validation, side effects, and background work.

### Creating one
**Customize → Server Script → New**:

| Script Type | When it runs |
|---|---|
| **DocType Event** | On a document lifecycle (validate, before_save, on_submit, on_cancel, on_trash) |
| **API Method** | Exposed at `/api/method/<your.path>` for external callers |
| **Scheduler Event** | On a cron schedule (hourly, daily, custom) |
| **Permission Query** | Returns a SQL fragment to filter list views |

### DocType event example

```python
# Triggered on validate of Sales Invoice
if doc.grand_total > 100000 and not doc.approved_by_manager:
    frappe.throw(_("Invoices over 100k require manager approval"))

if doc.customer:
    customer = frappe.get_doc("Customer", doc.customer)
    outstanding = customer.outstanding or 0
    if customer.credit_limit and (doc.grand_total + outstanding) > customer.credit_limit:
        frappe.throw(_("Customer over credit limit"))
```

`doc` is the current document; `frappe` is the framework module; `_()` translates strings.

### Scheduler example

```python
# Daily: notify accounting of overdue invoices
overdue = frappe.get_all("Sales Invoice", filters={
    "due_date": ["<", frappe.utils.nowdate()],
    "outstanding_amount": [">", 0],
    "docstatus": 1
})
if overdue:
    frappe.sendmail(
        recipients=["accounts@example.com"],
        subject=f"{len(overdue)} overdue invoices",
        message="<br>".join(i.name for i in overdue)
    )
```

### API method example

```python
# Callable at /api/method/<your.path> — args from request payload
return {"ok": True, "result": frappe.utils.flt(amount) * 1.21}
```

### Sandbox
Server Scripts run in a Python sandbox: no `import` of arbitrary modules, no file system writes, no subprocess. For unrestricted Python or external dependencies, write a Frappe app instead — see [Developers](/docs/frappe-ecosystem/developers).
