Tutorials/Integration Capability

Integration Capability

Summary: Frappe has a powerful, auto-generated REST API for all DocTypes, plus webhooks, event streaming, and a growing ecosystem of connectors. GST e-invoicing, payment gateways, and government API integrations are available via the India Compliance app and community apps.

  • ✅ REST API: Auto-generated REST API for every DocType at /api/resource/{doctype}. Full CRUD support.
  • ✅ Authentication: API key + secret token authentication. OAuth2 tokens also supported.
  • ⚠️ GraphQL: Available via frappe-graphql community app; not built into core.
  • ❌ SOAP: Not native; implement via custom Python wrapper using zeep or suds.
  • ✅ API Versioning: /api/method/ whitelisted methods; version management via app architecture.
  • ✅ Webhooks: Built-in Webhook doctype — configure per-DocType, per-event (create/update/submit). Outbound HTTP POST to any endpoint.
  • ✅ Event Streaming: Built-in Event Streaming (v12+) for cross-instance real-time sync.
  • 🔧 Message Queues: Redis Queue (RQ) for async tasks. Kafka integration possible for enterprise event streaming.
  • 🔧 ESB/EAI: No built-in ESB; integrate with Mulesoft/WSO2 via REST API.
  • ✅ Payment Gateways: Razorpay, PayPal, Stripe integrations available (Frappe Cloud Marketplace + community apps). Custom webhook-based integrations for others.
  • ✅ GST Portal (e-Invoicing): India Compliance app provides GST e-invoicing (IRN/QR code), e-way bills, GSTR-1/2B/3B reconciliation, and NIC portal integration.
  • ⚠️ Banking APIs: Manual bank statement import or third-party apps (e.g., Bank Integration apps in marketplace). Real-time bank feeds require custom integration.
  • 🔧 Aadhaar/DigiLocker: No native connector; custom Python app using government APIs (UID/UIDAI). Community solutions exist.
  • ✅ RPA: Compatible with UiPath/Automation Anywhere via REST API.

Webhooks — Real-Time Event Delivery

Frappe Framework includes a built-in Webhook system that pushes real-time notifications to external services whenever a document event occurs — no custom code required. It is configured entirely through the Admin UI.

Frappe Webhook Flow
Frappe Webhook — from document event to HTTP POST delivery via Redis Queue

How It Works

When a document event fires (e.g. a Sales Order is submitted), Frappe checks all configured Webhooks to find any that match the DocType and event. If matched, it enqueues an HTTP POST job in Redis Queue. A background Frappe Worker picks it up and delivers the payload to the configured endpoint URL.

Delivery is asynchronous — the user action completes immediately and the webhook fires in the background without blocking the UI.


Webhook Configuration

Each Webhook is a DocType record with the following settings:

Field Description
Webhook URL The external HTTPS endpoint to POST to
DocType Which DocType to watch (e.g. Sales Order, Employee, Payment Entry)
Document Events One or more events: after_insert, on_update, on_submit, on_cancel, on_trash
Conditions Optional Jinja2 or Python expression to filter — e.g. only fire if doc.status == "Submitted"
Request Headers Custom HTTP headers — use for Authorization: Bearer <token> or API key auth to the receiving system
Payload Template Jinja2 template to define exactly which fields are included in the JSON body
Enable / Disable Toggle without deleting the configuration

Payload

The default payload is a JSON object of the full document. Using the Payload Template, you can send only the fields the receiving system needs:

{
  "order_id": "{ doc.name }",
  "customer": "{ doc.customer }",
  "total": { doc.grand_total },
  "status": "{ doc.status }",
  "submitted_at": "{ doc.modified }"
}

Security

  • HTTPS only — Frappe enforces HTTPS endpoints for webhook delivery
  • Request Headers — pass bearer tokens, HMAC signatures, or API keys to authenticate with the receiving system
  • IP Whitelisting — the receiving system can whitelist Frappe's server IP for additional security

Retry & Failure Handling

  • If the endpoint returns a 4xx or 5xx response, Frappe logs the failure against the Webhook record
  • Failed deliveries are visible in the Webhook Request Log DocType — with status, response code, and response body
  • Administrators can manually re-trigger failed webhook requests from the log
  • Repeated failures do not block the originating document action


Real-World Example — Webhook in Hybrowlabs ERPNext

The screenshot below shows an actual Webhook configured on our ERPNext instance. This webhook fires on after_insert of the Job Applicant DocType and posts a JSON payload to an external automation endpoint.

Hybrowlabs ERPNext Webhook Configuration — Job Applicant after_insert
Live webhook configuration in Hybrowlabs ERPNext — Job Applicant DocType, after_insert event, POST to automation endpoint with JSON body using Jinja2 field placeholders

Key things to note from the configuration:

Field Value Notes
DocType Job Applicant Watches this specific DocType
Doc Event after_insert Fires when a new applicant is created
Enabled Active
Request Method POST Standard for webhooks
Request Structure JSON Payload format
Request Timeout 5s Fails fast if endpoint is down
Headers Content-Type: application/json Tells receiver to parse as JSON
JSON Body [ {"id":"{{ doc.name }}"} ] Jinja2 template — doc.name is the document ID
Webhook Request Log Linked All delivery attempts logged here

Common Use Cases

Integration Webhook Trigger What Happens
Slack / Teams notifications Sales Order on_submit A message is posted to a channel with order details
External CRM sync Lead after_insert New lead is pushed to Salesforce / HubSpot
Payment gateway confirmation Payment Entry on_submit External billing system is notified
Logistics system Delivery Note on_submit Shipment is triggered in 3PL system
Automation platforms Any event n8n / Zapier / Make picks up the event and runs a workflow
Custom backend Any event Internal microservice receives real-time data without polling the ERPNext API

Event Streaming vs Webhooks

Frappe also includes Event Streaming (available from v12+) for syncing data between two Frappe instances in real time — different from Webhooks which push to external non-Frappe systems.

Feature Webhooks Event Streaming
Target Any external HTTP endpoint Another Frappe / ERPNext instance
Protocol HTTP POST (JSON) Frappe-to-Frappe API
Use case Third-party integrations Multi-instance ERPNext deployments
Configuration Webhook DocType Event Producer / Consumer DocTypes

REST API Reference

📖 Full official docs: REST API Intro · Simple Auth · Token Auth · OAuth 2 · Listing · Manipulating

Frappe exposes two categories of HTTP endpoints:

Type Prefix Purpose
RPC /api/method/ Call any whitelisted Python method
REST /api/resource/ CRUD on any DocType
REST v2 /api/v2/document/ Richer REST interface (Frappe v15+)

Base URL: https://{your-erpnext-instance} — all paths below append to this.


Authentication

Three methods are available. API Token auth is recommended for server-to-server integrations.

# Login — returns a `sid` cookie valid for 3 days
curl -X POST https://{instance}/api/method/login \
  -H 'Content-Type: application/json' \
  -d '{"usr":"Administrator","pwd":"admin"}'

# Logout
curl https://{instance}/api/method/logout

Use only for browser-based or short-lived scripts.

Generate once per user: User list → Settings tab → API Access → Generate Keys. Copy the API Secret immediately.

# Header format
Authorization: token <api_key>:<api_secret>
import requests
headers = {"Authorization": "token efaaf2d8fbac138:f3fb3c4ea507531"}
r = requests.get("https://{instance}/api/resource/Customer/CUST-00001", headers=headers)

Create a dedicated API user with minimal roles — all requests run under that user's permissions.

3. OAuth 2 (Bearer Token)

For user-delegated access (third-party apps acting on behalf of an ERPNext user).

Step 1 — Redirect user to authorise:

GET /api/method/frappe.integrations.oauth2.authorize
  ?client_id=YOUR_CLIENT_ID&response_type=code&scope=openid%20all
  &redirect_uri=https://yourapp.com/callback&state=random_value

Step 2 — Exchange code for token:

curl -X POST https://{instance}/api/method/frappe.integrations.oauth2.get_token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code&code=AUTHCODE&client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/callback'
# Returns: access_token, refresh_token, expires_in (3600s)

Use the token: Authorization: Bearer <access_token>

Refresh: resend with grant_type=refresh_token&refresh_token=...

Auth Methods Compared

Method Best for Expiry
Session cookie One-off scripts, browser 3 days
API Token Server-to-server integrations Never
OAuth 2 Bearer Third-party / user-delegated apps 1 hour (refresh available)

Listing Documents

# Basic list (name only, paginated 20/page by default)
GET /api/resource/Customer

# Select fields
GET /api/resource/Customer?fields=["name","customer_name","phone"]

# Filter
GET /api/resource/Customer?filters=[["Customer","country","=","India"]]

# Paginate — page 2
GET /api/resource/Customer?limit_page_length=50&limit_start=50

Filter operators: = != < > <= >= like not like in not in is is not

Response: { "data": [ { "name": "CUST-00001", ... } ] }


CRUD Operations

# CREATE
curl -X POST https://{instance}/api/resource/Lead \
  -H 'Authorization: token key:secret' -H 'Content-Type: application/json' \
  -d '{"lead_name":"Mustermann","email_id":"m@example.com"}'

# READ
curl https://{instance}/api/resource/Customer/CUST-00001 \
  -H 'Authorization: token key:secret'

# UPDATE (acts as PATCH — send only changed fields)
curl -X PUT https://{instance}/api/resource/Lead/LEAD-00001 \
  -H 'Authorization: token key:secret' -H 'Content-Type: application/json' \
  -d '{"contact_date":"2026-04-01"}'

# DELETE
curl -X DELETE https://{instance}/api/resource/Customer/CUST-00001 \
  -H 'Authorization: token key:secret'

RPC — Call Whitelisted Methods

# Any method decorated with @frappe.whitelist() is callable
GET /api/method/frappe.ping                          # { "message": "pong" }
GET /api/method/frappe.auth.get_logged_user          # { "message": "Administrator" }

# Call a custom method
curl -X POST https://{instance}/api/method/myapp.api.create_invoice \
  -H 'Authorization: token key:secret' -H 'Content-Type: application/json' \
  -d '{"customer":"CUST-00001"}'

API v2 — Extended Operations (Frappe v15+)

Operation Endpoint
List GET /api/v2/document/{DocType}
Create POST /api/v2/document/{DocType}
Read GET /api/v2/document/{DocType}/{name}/
Update PATCH /api/v2/document/{DocType}/{name}/
Delete DELETE /api/v2/document/{DocType}/{name}/
Copy GET /api/v2/document/{DocType}/{name}/copy
Submit POST /api/v2/document/{DocType}/{name}/method/submit
Cancel POST /api/v2/document/{DocType}/{name}/method/cancel
Count GET /api/v2/doctype/{DocType}/count
Metadata GET /api/v2/doctype/{DocType}/meta

Integration Best Practices

  • Use API Token auth — stateless, no session management
  • Dedicated API user with minimal permissions per integration
  • Loop with limit_page_length + limit_start for bulk fetches — never assume 20 records is all there is
  • Handle 403 gracefully — the API user lacks permission for that DocType or document
  • Prefer webhooks over polling — configure after_insert webhook to push to your endpoint instead of polling for new records
  • Never use a production API key in dev/test scripts

Need help with your workflow setup?

If you're stuck or want help applying these guides to your setup, our team can assist with configuration, customization, and workflow implementation.

WhatsApp