Skip to content
AI & Agents
Skill

/frappe-core-api

Use when building ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate limiting. Covers external API calls, endpoint design, token/OAuth2/session authentication. Keywords: API integration, REST endpoint, webhook, token

From plugin
frappe-claude-skill-package
17861 skills
Install
$ npx -y skills add Impertio-Studio/Frappe_Claude_Skill_Package --skill frappe-core-api --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-core-api

Context preview

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

Use when building ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate limiting. Covers external API calls, endpoint design, token/OAuth2/session authentication. Keywords: API integration, REST endpoint, webhook, token

SKILL.md

frappe-core-api.SKILL.md
name: frappe-core-api
description: >
  Use when building ERPNext/Frappe API integrations (v14/v15/v16) including
  REST API, RPC API, authentication, webhooks, and rate limiting. Covers
  external API calls, endpoint design, token/OAuth2/session authentication.
  Keywords: API integration, REST endpoint, webhook, token authentication,, how to connect, external API, send data to another system, API not working, 401 error.
  OAuth, frappe.call, external connection, rate limiting.
license: MIT
compatibility: "Claude Code, Claude.ai Projects, Claude API. Frappe v14-v16."
metadata:
  author: OpenAEC-Foundation
  version: "2.0"

Frappe API Patterns

> Deterministic patterns for REST, RPC, and webhook integrations with Frappe.

---

Decision Tree

What do you need?
├── CRUD on documents (external client)
│   ├── v14: REST /api/resource/{doctype}
│   └── v15+: REST /api/v2/document/{doctype} (new) or /api/resource/ (still works)
│
├── Call custom server logic (external client)
│   └── RPC: POST /api/method/{dotted.path.to.function}
│
├── Notify external systems on document events
│   └── Webhooks (configured in UI or via DocType)
│
├── Client-side calls (JavaScript in Frappe desk)
│   ├── frappe.xcall() — async/await (RECOMMENDED)
│   └── frappe.call() — callback/promise pattern
│
└── Authentication method?
    ├── Server-to-server integration → Token Auth (RECOMMENDED)
    ├── Third-party app / mobile → OAuth 2.0
    ├── Browser session (short-lived) → Session/Cookie Auth
    └── Quick scripting / testing → Token Auth

---

Authentication Methods

Token Auth (RECOMMENDED for integrations)

headers = {
    'Authorization': 'token api_key:api_secret',
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}

Generate keys: User > Settings > API Access > Generate Keys. ALWAYS store API secret immediately — it is shown only once.

Basic Auth (alternative token format)

import base64
credentials = base64.b64encode(b'api_key:api_secret').decode()
headers = {'Authorization': f'Basic {credentials}'}

OAuth 2.0 (third-party apps)

# Step 1: Authorization redirect
GET /api/method/frappe.integrations.oauth2.authorize
    ?client_id={id}&response_type=code&scope=openid all
    &redirect_uri={uri}&state={random}

# Step 2: Exchange code for token
POST /api/method/frappe.integrations.oauth2.get_token
    grant_type=authorization_code&code={code}
    &redirect_uri={uri}&client_id={id}

# Step 3: Use bearer token
Authorization: Bearer {access_token}

# Refresh token
POST /api/method/frappe.integrations.oauth2.get_token
    grant_type=refresh_token&refresh_token={token}&client_id={id}

Session/Cookie Auth

session = requests.Session()
session.post(url + '/api/method/login', json={'usr': 'email', 'pwd': 'pass'})
# Subsequent requests use session cookie automatically

Session cookies expire after ~3 days. NEVER use for long-running integrations.

---

REST API: Resource CRUD

Endpoints

| Operation | Method | v14 Endpoint | v15+ v2 Endpoint | |-----------|--------|--------------|------------------| | List | GET | `/api/resource/{doctype}` | `/api/v2/document/{doctype}` | | Create | POST | `/api/resource/{doctype}` | `/api/v2/document/{doctype}` | | Read | GET | `/api/resource/{doctype}/{name}` | `/api/v2/document/{doctype}/{name}` | | Update | PUT | `/api/resource/{doctype}/{name}` | PATCH `/api/v2/document/{doctype}/{name}` | | Delete | DELETE | `/api/resource/{doctype}/{name}` | DELETE `/api/v2/document/{doctype}/{name}` | | Copy | — | — | GET `/api/v2/document/{doctype}/{name}/copy` [v15+] | | Doc Method | — | — | POST `/api/v2/document/{doctype}/{name}/method/{method}` [v15+] |

**ALWAYS** include `Accept: application/json` header — without it, Frappe MAY return HTML.

List Parameters

| Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `fields` | JSON array | Fields to return | `["name"]` | | `filters` | JSON array | AND conditions | none | | `or_filters` | JSON array | OR conditions | none | | `order_by` | string | Sort expression | `modified desc` | | `limit_start` | int | Pagination offset | `0` | | `limit_page_length` | int | Page size | `20` | | `limit` | int | Alias for limit_page_length [v15+] | — | | `debug` | bool | Show SQL in response | `false` |

Filter Operators

filters = [["status", "=", "Open"]]
filters = [["amount", ">", 1000]]
filters = [["status", "in", ["Open", "Pending"]]]
filters = [["date", "between", ["2024-01-01", "2024-12-31"]]]
filters = [["reference", "is", "set"]]       # NOT NULL
filters = [["reference", "is", "not set"]]   # IS NULL
filters = [["name", "like", "%INV%"]]
filters = [["status", "not in", ["Cancelled"]]]

Full operator list: `=`, `!=`, `>`, `<`, `>=`, `<=`, `like`, `not like`, `in`, `not in`, `is`, `between`.

Pagination Pattern

import json, requests

def get_all_records(doctype, headers, base_url, page_size=100):
    all_data, offset = [], 0
    while True:
        params = {
            'fields': json.dumps(["name", "modified"]),
            'limit_start': offset,
            'limit_page_length': page_size
        }
        resp = requests.get(f'{base_url}/api/resource/{doctype}',
                            params=params, headers=headers)
        data = resp.json().get('data', [])
        if not data:
            break
        all_data.extend(data)
        if len(data) < page_size:
            break
        offset += page_size
    return all_data

Create with Child Table

requests.post(f'{base_url}/api/resource/Sales Order', json={
    "customer": "CUST-001",
    "items": [
        {"item_code": "ITEM-001", "qty": 5, "rate": 100},
        {"item_code": "ITEM-002", "qty": 2, "rate": 250}
    ]
}, headers=headers)

Update (Partial)

# Only specified fields are changed
requests.put(f'{base_url}/api/resource/Customer/CUST-001',
             j
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
13d ago
Added

Repo: Impertio-Studio/Frappe_Claude_Skill_Package