Skip to content
frappe-api logo

Frappe API Reference

frappe-api

Frappe Python and JavaScript API reference including document operations, database queries, utilities, and REST API patterns. Use when working with frappe.get_doc, frappe.db, frappe.call, or any Frappe API methods.

SKILL.md

Full skill instructions

Frappe API Reference

Complete reference for Frappe's Python and JavaScript APIs for document operations, database queries, utilities, and server communication.

When to Use This Skill

  • Working with Document API (get_doc, new_doc, save)
  • Database operations (frappe.db.*)
  • Making API calls from client to server
  • Using Frappe utilities (date, number formatting)
  • Creating whitelisted API endpoints
  • Working with REST API

Python API

Document Operations

Get Document
# Get existing document
doc = frappe.get_doc("Customer", "CUST-001")

# Get document with filters
doc = frappe.get_doc("Customer", {"customer_name": "John"})

# Get last document
doc = frappe.get_last_doc("Customer", filters={"status": "Active"})

# Get cached document (read-only, faster)
doc = frappe.get_cached_doc("Customer", "CUST-001")

# Check if document exists
if frappe.db.exists("Customer", "CUST-001"):
    doc = frappe.get_doc("Customer", "CUST-001")
Create Document
# Create new document
doc = frappe.new_doc("Customer")
doc.customer_name = "New Customer"
doc.customer_type = "Company"
doc.insert()

# Create with dict
doc = frappe.get_doc({
    "doctype": "Customer",
    "customer_name": "New Customer",
    "customer_type": "Company"
})
doc.insert()

# Create and insert in one step
doc = frappe.get_doc({
    "doctype": "Customer",
    "customer_name": "New Customer"
}).insert()

# Insert ignoring permissions
doc.insert(ignore_permissions=True)

# Insert ignoring mandatory fields
doc.insert(ignore_mandatory=True)
Update Document
# Update and save
doc = frappe.get_doc("Customer", "CUST-001")
doc.customer_name = "Updated Name"
doc.save()

# Save ignoring permissions
doc.save(ignore_permissions=True)

# Update single value
frappe.db.set_value("Customer", "CUST-001", "customer_name", "New Name")

# Update multiple values
frappe.db.set_value("Customer", "CUST-001", {
    "customer_name": "New Name",
    "status": "Active"
})

# Bulk update
frappe.db.set_value("Customer", {"status": "Inactive"}, "status", "Active")
Delete Document
# Delete document
frappe.delete_doc("Customer", "CUST-001")

# Delete ignoring permissions
frappe.delete_doc("Customer", "CUST-001", ignore_permissions=True)

# Delete with linked documents
frappe.delete_doc("Customer", "CUST-001", force=True)

# Delete from controller
doc.delete()

Database API (frappe.db)

Select Queries
# Get single value
value = frappe.db.get_value("Customer", "CUST-001", "customer_name")

# Get multiple fields
values = frappe.db.get_value("Customer", "CUST-001",
    ["customer_name", "status"], as_dict=True)

# Get with filters
value = frappe.db.get_value("Customer",
    {"customer_type": "Company"}, "customer_name")

# Get list of values
names = frappe.db.get_all("Customer",
    filters={"status": "Active"},
    fields=["name", "customer_name"],
    order_by="creation desc",
    limit=10
)

# Get list with pluck (single field as list)
names = frappe.db.get_all("Customer",
    filters={"status": "Active"},
    pluck="name"
)

# Complex filters
docs = frappe.db.get_all("Sales Invoice",
    filters={
        "status": ["in", ["Paid", "Unpaid"]],
        "grand_total": [">", 1000],
        "posting_date": ["between", ["2024-01-01", "2024-12-31"]],
        "customer": ["like", "%Corp%"]
    },
    fields=["name", "customer", "grand_total"]
)

# Filter operators
# =, !=, <, >, <=, >=
# in, not in
# like, not like
# between
# is, is not (for None)
# descendants of, ancestors of (for tree doctypes)

# Get count
count = frappe.db.count("Customer", {"status": "Active"})

# Check existence
exists = frappe.db.exists("Customer", "CUST-001")
exists = frappe.db.exists("Customer", {"customer_name": "John"})
Raw SQL
# Execute SQL query
result = frappe.db.sql("""
    SELECT name, customer_name, grand_total
    FROM `tabSales Invoice`
    WHERE status = %s AND grand_total > %s
    ORDER BY creation DESC
    LIMIT 10
""", ("Paid", 1000), as_dict=True)

# Single value
total = frappe.db.sql("""
    SELECT SUM(grand_total) FROM `tabSales Invoice`
    WHERE status = 'Paid'
""")[0][0]

# With named parameters
result = frappe.db.sql("""
    SELECT * FROM `tabCustomer`
    WHERE name = %(name)s
""", {"name": "CUST-001"}, as_dict=True)
Insert/​Update
# Insert raw
frappe.db.sql("""
    INSERT INTO `tabCustomer` (name, customer_name)
    VALUES (%s, %s)
""", ("CUST-002", "New Customer"))

# Commit transaction
frappe.db.commit()

# Rollback
frappe.db.rollback()

Whitelisted API

# Create API endpoint
@frappe.whitelist()
def get_customer_details(customer):
    """Get customer details

    Args:
        customer: Customer ID

    Returns:
        dict: Customer details
    """
    doc = frappe.get_doc("Customer", customer)
    return {
        "name": doc.name,
        "customer_name": doc.customer_name,
        "outstanding_amount": get_outstanding(customer)
    }

# Allow guest access (no login required)
@frappe.whitelist(allow_guest=True)
def public_api():
    return {"status": "ok"}

# With specific methods
@frappe.whitelist(methods=["POST"])
def create_record(data):
    doc = frappe.get_doc(data)
    doc.insert()
    return doc.name

Utilities

Date/​Time
from frappe.utils import (
    now, nowdate, nowtime, now_datetime,
    today, getdate, get_datetime,
    add_days, add_months, add_years,
    date_diff, time_diff, time_diff_in_seconds,
    get_first_day, get_last_day,
    formatdate, format_datetime
)

# Current date/​time
current = nowdate()  # "2024-01-15"
current_dt = now_datetime()  # datetime object
timestamp = now()  # "2024-01-15 10:30:00"

# Date arithmetic
next_week = add_days(nowdate(), 7)
next_month = add_months(nowdate(), 1)
last_year = add_years(nowdate(), -1)

# Date difference
days = date_diff(end_date, start_date)
seconds = time_diff_in_seconds(end_time, start_time)

# First/​last day of month
first = get_first_day(nowdate())
last = get_last_day(nowdate())

# Parse dates
date_obj = getdate("2024-01-15")
dt_obj = get_datetime("2024-01-15 10:30:00")

# Format dates
formatted = formatdate("2024-01-15", "dd-MM-yyyy")
Numbers
from frappe.utils import (
    flt, cint, cstr,
    fmt_money, rounded,
    money_in_words
)

# Type conversion with defaults
num = flt(value)  # float, None -> 0.0
num = flt(value, 2)  # with precision
integer = cint(value)  # int, None -> 0
string = cstr(value)  # string, None -> ""

# Formatting
formatted = fmt_money(1234.56, currency="USD")  # "$1,234.56"
rounded_val = rounded(1234.567, 2)  # 1234.57
words = money_in_words(1234.56, "USD")  # "One Thousand..."
Strings
from frappe.utils import (
    strip_html, strip_html_tags,
    escape_html, sanitize_html,
    scrub, unscrub
)

# HTML handling
plain = strip_html("<p>Hello</​p>")  # "Hello"
safe = escape_html("<script>bad</​script>")

# Field name conversion
field = scrub("My Field Name")  # "my_field_name"
label = unscrub("my_field_name")  # "My Field Name"

Messaging & Notifications

# Show message (appears as toast)
frappe.msgprint("Document saved successfully")

# With indicator
frappe.msgprint("Error occurred", indicator="red", title="Error")

# Throw error (stops execution)
frappe.throw("Invalid data provided")

# With exception type
from frappe.exceptions import ValidationError
frappe.throw("Validation failed", exc=ValidationError)

# Send email
frappe.sendmail(
    recipients=["[email protected]"],
    subject="Hello",
    message="Email body",
    template="email_template",
    args={"name": "John"}
)

# Create system notification
frappe.publish_realtime(
    "msgprint",
    {"message": "Task completed"},
    user="[email protected]"
)

Background Jobs

# Enqueue background job
frappe.enqueue(
    "myapp.tasks.heavy_task",
    queue="long",
    timeout=600,
    job_name="Heavy Task",
    customer="CUST-001"
)

# In tasks.py
def heavy_task(customer):
    # Long running task
    process_customer(customer)

# Enqueue with callback
frappe.enqueue(
    method=process_data,
    queue="default",
    on_success=on_complete,
    on_failure=on_error
)

# Scheduled jobs (in hooks.py)
scheduler_events = {
    "daily": [
        "myapp.tasks.daily_task"
    ],
    "hourly": [
        "myapp.tasks.hourly_task"
    ],
    "cron": {
        "0 0 * * *": [  # Midnight
            "myapp.tasks.midnight_task"
        ]
    }
}

Session & User

# Current user
user = frappe.session.user

# Check if logged in
if frappe.session.user != "Guest":
    pass

# Check permissions
if frappe.has_permission("Customer", "write"):
    pass

# Get user info
user_doc = frappe.get_doc("User", frappe.session.user)
full_name = frappe.utils.get_fullname(frappe.session.user)

# Check roles
if "System Manager" in frappe.get_roles():
    pass

# Run as different user
frappe.set_user("Administrator")
# ... do operations
frappe.set_user(original_user)

JavaScript API

Document Operations

// Get document
frappe.call({
    method: 'frappe.client.get',
    args: {
        doctype: 'Customer',
        name: 'CUST-001'
    },
    callback: function(r) {
        console.log(r.message);
    }
});

// Create document
frappe.call({
    method: 'frappe.client.insert',
    args: {
        doc: {
            doctype: 'Customer',
            customer_name: 'New Customer'
        }
    },
    callback: function(r) {
        console.log('Created:', r.message.name);
    }
});

// Save document
frappe.call({
    method: 'frappe.client.save',
    args: {
        doc: cur_frm.doc
    }
});

// Delete document
frappe.call({
    method: 'frappe.client.delete',
    args: {
        doctype: 'Customer',
        name: 'CUST-001'
    }
});

frappe.call (API Calls)

// Call whitelisted method
frappe.call({
    method: 'myapp.api.get_customer_details',
    args: {
        customer: 'CUST-001'
    },
    freeze: true,
    freeze_message: 'Loading...',
    callback: function(r) {
        if (r.message) {
            console.log(r.message);
        }
    },
    error: function(r) {
        frappe.msgprint('Error occurred');
    }
});

// Async/​await pattern
async function getCustomer(name) {
    const response = await frappe.call({
        method: 'myapp.api.get_customer',
        args: { name }
    });
    return response.message;
}

// Call with promise
frappe.call({
    method: 'myapp.api.process',
    args: { data: 'test' }
}).then(r => {
    console.log(r.message);
});

Form API (cur_frm)

frappe.ui.form.on('Sales Invoice', {
    refresh: function(frm) {
        // Add custom button
        frm.add_custom_button('Process', function() {
            // Button action
        }, 'Actions');

        // Set field properties
        frm.set_df_property('field_name', 'read_only', 1);
        frm.set_df_property('field_name', 'hidden', 1);
        frm.set_df_property('field_name', 'reqd', 1);

        // Set query for link field
        frm.set_query('customer', function() {
            return {
                filters: {
                    status: 'Active'
                }
            };
        });

        // Toggle fields
        frm.toggle_display('field_name', frm.doc.show_field);
        frm.toggle_reqd('field_name', frm.doc.is_required);
    },

    customer: function(frm) {
        // Field change handler
        if (frm.doc.customer) {
            frappe.call({
                method: 'myapp.api.get_customer_details',
                args: { customer: frm.doc.customer },
                callback: function(r) {
                    frm.set_value('customer_name', r.message.name);
                }
            });
        }
    },

    validate: function(frm) {
        // Validate before save
        if (!frm.doc.customer) {
            frappe.throw('Customer is required');
            return false;
        }
    },

    before_save: function(frm) {
        // Before save actions
        frm.doc.modified_by_script = 1;
    },

    after_save: function(frm) {
        // After save actions
        frappe.show_alert('Document saved!');
    }
});

// Child table events
frappe.ui.form.on('Sales Invoice Item', {
    qty: function(frm, cdt, cdn) {
        let row = locals[cdt][cdn];
        row.amount = row.qty * row.rate;
        frm.refresh_field('items');
    },

    items_add: function(frm, cdt, cdn) {
        // New row added
        let row = locals[cdt][cdn];
        row.warehouse = frm.doc.default_warehouse;
    },

    items_remove: function(frm) {
        // Row removed - recalculate totals
        calculate_totals(frm);
    }
});

Dialogs

// Simple prompt
frappe.prompt(
    {fieldname: 'name', fieldtype: 'Data', label: 'Name', reqd: 1},
    function(values) {
        console.log(values.name);
    },
    'Enter Name'
);

// Multiple fields
frappe.prompt([
    {fieldname: 'name', fieldtype: 'Data', label: 'Name', reqd: 1},
    {fieldname: 'email', fieldtype: 'Data', label: 'Email', options: 'Email'},
    {fieldname: 'date', fieldtype: 'Date', label: 'Date', default: frappe.datetime.nowdate()}
], function(values) {
    console.log(values);
}, 'Enter Details', 'Submit');

// Custom dialog
let dialog = new frappe.ui.Dialog({
    title: 'My Dialog',
    fields: [
        {fieldname: 'customer', fieldtype: 'Link', options: 'Customer', label: 'Customer'},
        {fieldname: 'amount', fieldtype: 'Currency', label: 'Amount'}
    ],
    primary_action_label: 'Submit',
    primary_action: function(values) {
        console.log(values);
        dialog.hide();
    }
});
dialog.show();

// Confirmation
frappe.confirm(
    'Are you sure you want to proceed?',
    function() {
        // Yes
        process_action();
    },
    function() {
        // No
    }
);

Utilities

// Messages
frappe.msgprint('Hello World');
frappe.msgprint({
    title: 'Success',
    message: 'Operation completed',
    indicator: 'green'
});

frappe.throw('Error message');  // Stops execution

frappe.show_alert('Quick notification', 5);  // 5 seconds

// Date/​time
frappe.datetime.nowdate();  // "2024-01-15"
frappe.datetime.now_datetime();  // "2024-01-15 10:30:00"
frappe.datetime.add_days('2024-01-15', 7);
frappe.datetime.str_to_obj('2024-01-15');

// Format
frappe.format(1234.56, {fieldtype: 'Currency'});
frappe.format('2024-01-15', {fieldtype: 'Date'});

// Routing
frappe.set_route('Form', 'Customer', 'CUST-001');
frappe.set_route('List', 'Customer');
frappe.set_route('query-report', 'Sales Report');

// Current route
let route = frappe.get_route();

REST API

Authentication

# Token-based
curl -X GET "https://site.com/​api/​resource/​Customer" \
  -H "Authorization: token api_key:api_secret"

# Session-based (login first)
curl -X POST "https://site.com/​api/​method/​login" \
  -d "usr=user&pwd=password"

CRUD Operations

# List
GET /​api/​resource/​Customer?filters=[["status","=","Active"]]&fields=["name","customer_name"]&limit_page_length=10

# Get single
GET /​api/​resource/​Customer/​CUST-001

# Create
POST /​api/​resource/​Customer
Content-Type: application/​json
{"customer_name": "New Customer", "customer_type": "Company"}

# Update
PUT /​api/​resource/​Customer/​CUST-001
Content-Type: application/​json
{"customer_name": "Updated Name"}

# Delete
DELETE /​api/​resource/​Customer/​CUST-001

Call Methods

# Call whitelisted method
POST /​api/​method/​myapp.api.get_customer_details
Content-Type: application/​json
{"customer": "CUST-001"}

JavaScript Fetch

// Using fetch API
async function getCustomers() {
    const response = await fetch('/​api/​resource/​Customer?limit_page_length=10', {
        headers: {
            'Content-Type': 'application/​json'
        }
    });
    const data = await response.json();
    return data.data;
}

// POST request
async function createCustomer(customerData) {
    const response = await fetch('/​api/​resource/​Customer', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/​json'
        },
        body: JSON.stringify(customerData)
    });
    return response.json();
}

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View