Back to skills
extension
Category: Development & EngineeringNo API key required

erpnext-api-patterns

Complete guide for ERPNext/Frappe API integrations (v14/v15/v16) including REST API, RPC API, authentication, webhooks, and rate limiting. Use when code is needed for external API calls to ERPNext, designing API endpoints, configuring webhooks, implementing authentication (token/OAuth2/session). Triggers: API integration, REST endpoint, webhook, token authentication, OAuth, frappe.call, external connection, API response, rate limiting.

personAuthor: jakexiaohubgithub

ERPNext API Patterns

API Type Decision Tree

What do you want to achieve?
│
├─► CRUD operations on documents
│   └─► REST API: /api/resource/{doctype}
│
├─► Call custom business logic
│   └─► RPC API: /api/method/{path}
│
├─► Notify external systems on events
│   └─► Configure Webhooks
│
└─► Client-side server calls (JavaScript)
    └─► frappe.call() or frappe.xcall()

Quick Reference

Authentication Headers

# Token Auth (RECOMMENDED for integrations)
headers = {
    'Authorization': 'token api_key:api_secret',
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}

# Bearer Token (OAuth2)
headers = {'Authorization': 'Bearer {access_token}'}

REST API CRUD

| Operation | Method | Endpoint | |-----------|--------|----------| | List | GET | /api/resource/{doctype} | | Create | POST | /api/resource/{doctype} | | Read | GET | /api/resource/{doctype}/{name} | | Update | PUT | /api/resource/{doctype}/{name} | | Delete | DELETE | /api/resource/{doctype}/{name} |

Filter Operators

# Basic filters
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

RPC Method Call

# Server-side: mark with decorator
@frappe.whitelist()
def my_function(param1, param2):
    return {"result": "value"}

# API call
POST /api/method/my_app.api.my_function
{"param1": "value1", "param2": "value2"}

Client-Side Calls (JavaScript)

// Async/await pattern (RECOMMENDED)
const result = await frappe.xcall('my_app.api.my_function', {
    param1: 'value'
});

// Promise pattern
frappe.call({
    method: 'my_app.api.my_function',
    args: {param1: 'value'},
    freeze: true,
    freeze_message: __('Processing...')
}).then(r => console.log(r.message));

Response Structure

REST API Success:

{"data": {...}}

RPC API Success:

{"message": "return_value"}

Error Response:

{
    "exc_type": "ValidationError",
    "_server_messages": "[{\"message\": \"Error details\"}]"
}

HTTP Status Codes

| Code | Meaning | |------|---------| | 200 | Success | | 400 | Validation error | | 401 | No authentication | | 403 | No permissions | | 404 | Document not found | | 417 | Server exception | | 429 | Rate limit exceeded |

Critical Rules

  1. ALWAYS include Accept: application/json header
  2. ALWAYS add permission checks in whitelisted methods
  3. NEVER hardcode credentials - use frappe.conf
  4. NEVER write SQL injection vulnerable queries
  5. GET for read-only, POST for state-changing operations

Reference Files

| File | Contents | |------|----------| | authentication-methods.md | Token, Session, OAuth2 implementation | | rest-api-reference.md | Complete REST API with filters and pagination | | rpc-api-reference.md | Whitelisted methods and frappe.call patterns | | webhooks-reference.md | Webhook configuration and security | | anti-patterns.md | Common mistakes and fixes |

Version Notes (v14 vs v15)

| Feature | v14 | v15 | |---------|-----|-----| | expand_links parameter | ❌ | ✅ | | Server Script rate limiting | ❌ | ✅ | | PKCE for OAuth2 | Limited | ✅ |