Skip to content
AI & Agents
Skill

/frappe-syntax-whitelisted

Use when creating Frappe Whitelisted Methods (Python API endpoints) for v14/v15/v16. Covers @frappe.whitelist() decorator, frappe.call/frm.call invocations, permission checks, error handling, response formats, and client-server communication. Keywords: whitelisted, API endpoint,

From plugin
frappe-claude-skill-package
17961 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-syntax-whitelisted --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/frappe-syntax-whitelisted

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when creating Frappe Whitelisted Methods (Python API endpoints) for v14/v15/v16. Covers @frappe.whitelist() decorator, frappe.call/frm.call invocations, permission checks, error handling, response formats, and client-server communication. Keywords: whitelisted, API endpoint,

SKILL.md

frappe-syntax-whitelisted.SKILL.md
name: frappe-syntax-whitelisted
description: >
  Use when creating Frappe Whitelisted Methods (Python API endpoints) for
  v14/v15/v16. Covers @frappe.whitelist() decorator, frappe.call/frm.call
  invocations, permission checks, error handling, response formats, and
  client-server communication. Keywords: whitelisted, API endpoint,
  frappe.call, frm.call, REST API, @frappe.whitelist, allow_guest,
  API endpoint example, frappe.whitelist syntax, how to expose function.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Frappe Syntax: Whitelisted Methods

Whitelisted methods expose Python functions as HTTP API endpoints via `/api/method/`.

Quick Reference

import frappe
from frappe import _

# Authenticated endpoint (default)
@frappe.whitelist()
def get_customer_summary(customer):
    frappe.has_permission("Customer", "read", throw=True)
    return frappe.get_doc("Customer", customer).as_dict()

# Public endpoint — ALWAYS validate input thoroughly
@frappe.whitelist(allow_guest=True, methods=["POST"])
def submit_contact(name, email, message):
    if not name or not email:
        frappe.throw(_("Name and email required"), frappe.ValidationError)
    return {"success": True}

# Controller method — called via frm.call('method_name')
class SalesOrder(Document):
    @frappe.whitelist()
    def calculate_taxes(self, include_shipping=False):
        return {"tax": self.grand_total * 0.21}

**Endpoint URL**: `/api/method/myapp.module.function_name`

---

Decorator Signature [v14+]

@frappe.whitelist(
    allow_guest=False,   # True = accessible without login
    xss_safe=False,      # True = do NOT escape HTML in response
    methods=None,        # ["GET"], ["POST"], or ["GET","POST"] — default: all
    force_types=None     # True = require type annotations [v15+]
)

| Parameter | Default | Effect | |-----------|---------|--------| | `allow_guest` | `False` | `True` = Guest role can call; ALWAYS add extra input validation | | `xss_safe` | `False` | `True` = HTML not escaped; NEVER use without sanitized output | | `methods` | `None` (all) | Restrict allowed HTTP verbs | | `force_types` | `None` | `True` = all params MUST have type annotations [v15+] |

Full details: [decorator-options.md](references/decorator-options.md)

---

Decision Tree

What kind of endpoint?
|
+-- Standalone API (utility, integration, dashboard)?
|   --> @frappe.whitelist() on a module-level function
|   --> Call via: frappe.call('myapp.api.function')
|   --> URL: /api/method/myapp.api.function
|
+-- Document-specific action?
|   --> @frappe.whitelist() on a Document class method
|   --> Call via: frm.call('method_name')
|   --> URL: /api/method/run_doc_method (internal)
|
+-- Server Script (no-code)?
    --> Use Server Script DocType instead (no decorator needed)

Who may call the API?
|
+-- Anyone (including guests)?
|   --> allow_guest=True + thorough input validation + rate limiting
|
+-- Logged-in users only?
    +-- Specific role? --> frappe.only_for("RoleName")
    +-- DocType-level? --> frappe.has_permission(doctype, ptype, throw=True)
    +-- Document-level? --> frappe.has_permission(doctype, ptype, doc, throw=True)

Which HTTP methods?
|
+-- Read only? --> methods=["GET"]
+-- Write only? --> methods=["POST"]
+-- Both? --> methods=["GET","POST"] or default

---

Permission Patterns

ALWAYS check permissions inside every whitelisted method. The `@frappe.whitelist()` decorator only verifies the user is logged in — it does NOT check DocType or document-level permissions.

# DocType-level permission (throw=True raises PermissionError automatically)
@frappe.whitelist()
def get_orders():
    frappe.has_permission("Sales Order", "read", throw=True)
    return frappe.get_all("Sales Order", limit=20)

# Document-level permission
@frappe.whitelist()
def get_order(name):
    frappe.has_permission("Sales Order", "read", name, throw=True)
    return frappe.get_doc("Sales Order", name).as_dict()

# Role-based restriction
@frappe.whitelist()
def admin_action():
    frappe.only_for("System Manager")  # throws if user lacks role
    return {"secret": "data"}

Full patterns: [permission-patterns.md](references/permission-patterns.md)

---

Parameter Handling

Parameters arrive as **strings** from HTTP requests. ALWAYS convert explicitly.

@frappe.whitelist()
def calculate(amount, quantity, items=None):
    amount = float(amount)          # ALWAYS cast numeric params
    quantity = int(quantity)
    if isinstance(items, str):      # ALWAYS parse JSON strings
        items = frappe.parse_json(items)
    return amount * quantity

Access all request parameters via `frappe.form_dict`:

@frappe.whitelist()
def dynamic_handler():
    all_params = frappe.form_dict
    customer = frappe.form_dict.get("customer")

Type Annotations [v15+]

Frappe v15+ validates type annotations automatically at request time via Pydantic:

@frappe.whitelist()
def get_orders(customer: str, limit: int = 10, active: bool = True) -> dict:
    # Frappe auto-validates: limit MUST be convertible to int
    return {"orders": frappe.get_all("Sales Order", limit=limit)}

force_types and require_type_annotated_api_methods [v15+]

  • `@frappe.whitelist(force_types=True)` — EVERY parameter MUST have a type annotation
  • App-level enforcement via `hooks.py`: `require_type_annotated_api_methods = 1`
  • Missing annotations raise `FrappeTypeError`

Full details: [parameter-handling.md](references/parameter-handling.md)

---

Client Calls

frappe.call(): Standalone APIs

// Promise-based (ALWAYS prefer this)
frappe.call({
    method: 'myapp.api.get_summary',
    args: { customer: 'CUST-001' },
    freeze: true,
    freeze_message: __('Loading...')
}).then(r => {
    console.log(r.message);  // return value is in r.message
}).catch(err => {
Read more
Ships withfrappe-claude-skill-package

60 deterministic Claude AI skills for Frappe Framework & ERPNext v14-v16 development and operations

Get the whole plugin
Stats
178
Stars
53
Forks
Maintained
Maintenance
Python
Language
2mo ago
Last commit
8mo ago
Created
14d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package