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.