一键导入
frappe-webhook-manager
Create webhook handlers for Frappe integrations. Use when implementing webhooks, event-driven integrations, or external system notifications.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create webhook handlers for Frappe integrations. Use when implementing webhooks, event-driven integrations, or external system notifications.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Plan and design a DocType architecture / data model for a new Frappe feature like a seasoned lead developer — interview the user, map entities, propose DocType names and links, and draw a flowchart of how the doctypes connect. Use this skill when the user says any of "help me plan a doctype architecture", "design a data model for", "what doctypes do I need for", "model a feature", "I want to build <feature>", "I need to implement an evaluation system", "issuing certificates", "design the schema for", "how should I structure my doctypes", "map the entities for", "plan the doctypes for a <X> system" (CRM, helpdesk, ticketing, LMS, booking, approval, subscription, inventory, etc.). It GRILLS the user with staged questions, proposes sensible defaults, keeps a running model, scans the existing app codebase to REUSE or EXTEND existing doctypes (adding fields / Custom Fields) before creating new ones, then produces a Mermaid diagram + per-DocType spec tables and offers to hand off to frappe-doctype-builder to genera
Create custom API endpoints and whitelisted methods for Frappe applications. Use when building REST APIs or custom endpoints.
Generate JavaScript client-side form scripts for Frappe DocTypes. Use when creating form customizations, field validations, custom buttons, or client-side logic for Frappe/ERPNext forms.
Generate data migration scripts for Frappe. Use when migrating data from legacy systems, transforming data structures, or importing large datasets.
Build Frappe DocTypes with fields, permissions, and naming configurations. Use this skill when creating or modifying DocType structures.
Generate API documentation, user guides, and technical documentation for Frappe apps. Use when documenting APIs, creating user guides, or generating OpenAPI specs.
| name | frappe-webhook-manager |
| description | Create webhook handlers for Frappe integrations. Use when implementing webhooks, event-driven integrations, or external system notifications. |
Generate secure webhook receivers and senders for Frappe integrations with external systems.
These Frappe conventions apply to everything this skill generates, and override any conflicting example below.
bench (never ./env/bin/bench or a full path). Always pass --site <site> explicitly — never run a bare bench migrate / bench run-tests. Run bench start in the background and only if it isn't already running. Don't run discovery commands (which bench, bench --version).apps/<app>/<app>/<module>/doctype/<name>/<name>.json — the app name appears twice (directory + Python package) — with an empty __init__.py alongside. Never mkdir the folder; write the JSON and run bench --site <site> migrate to create the structure. Don't add creation, modified, owner, modified_by, or docstatus as fields — Frappe manages them.frappe.qb.get_query() over raw frappe.db.sql(). Use frappe.db.get_all() for server logic (ignores permissions) and frappe.db.get_list() for user-facing APIs (enforces them). Never use frappe.db.set_value() on a field with validation or lifecycle logic — load the doc and doc.save() so controller hooks run. Batch-fetch related records; never query inside a loop (N+1).frappe.db.commit() in controllers, request handlers, background jobs, or patches — Frappe auto-commits on success and rolls back on uncaught errors. Flush manually only to make a write visible to a subsequent frappe.enqueue() (or pass enqueue_after_commit=True).@frappe.whitelist() parameter so Frappe validates and casts it, and pass methods=[...] to pin the HTTP verb.Claude should invoke this skill when:
Secure Webhook Endpoint: verify the signature BEFORE trusting any of the payload, and pin the verb to POST.
import frappe
from frappe import _
import hmac
import hashlib
@frappe.whitelist(allow_guest=True, methods=["POST"])
def webhook_receiver():
"""Receive webhook from external service"""
# Get signature
signature = frappe.get_request_header('X-Webhook-Signature') or ''
# Verify signature against the raw body FIRST — never trust the payload before this
secret = frappe.conf.get('webhook_secret')
expected = hmac.new(
secret.encode(),
frappe.request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
frappe.throw(_('Invalid signature'), frappe.AuthenticationError)
# Only now is it safe to parse the payload
payload = frappe.parse_json(frappe.request.data)
# Process webhook (offload anything slow to a background job)
event = payload.get('event')
if event == 'payment.success':
handle_payment_success(payload)
elif event == 'customer.updated':
handle_customer_update(payload)
# Return only a status — never echo sensitive fields back
return {'status': 'success'}
Trigger outbound webhooks from doc_events in hooks.py rather than inside the DocType controller, and enqueue delivery — never send inline inside the document event (a slow/failed receiver must not block or roll back the save).
Wire the trigger in hooks.py (handlers are dotted paths to importable functions):
# apps/myapp/myapp/hooks.py
doc_events = {
"Sales Invoice": {
"on_submit": "myapp.webhooks.queue_invoice_webhook",
}
}
Enqueue delivery (the handler receives doc and the event method):
# apps/myapp/myapp/webhooks.py
import frappe
def queue_invoice_webhook(doc, method):
"""doc_events handler: enqueue, don't send inline."""
frappe.enqueue(
"myapp.webhooks.send_webhook",
queue="short",
timeout=120,
job_id=f"webhook::{doc.name}::invoice.submitted", # stable id
deduplicate=True, # skip if an identical job is already queued
enqueue_after_commit=True, # only fire after the save commits
event="invoice.submitted",
data=doc.as_dict(),
)
Send with HMAC signature and a bounded retry:
# apps/myapp/myapp/webhooks.py
import frappe
import hmac
import hashlib
import json
import requests
def send_webhook(event, data):
"""Runs in a worker. No frappe.db.commit() — Frappe auto-commits on success."""
webhook_url = frappe.conf.get("external_webhook_url")
secret = frappe.conf.get("webhook_secret")
payload = {"event": event, "data": data, "timestamp": frappe.utils.now()}
body = json.dumps(payload)
signature = hmac.new(secret.encode(), body.encode(), hashlib.sha256).hexdigest()
headers = {"Content-Type": "application/json", "X-Webhook-Signature": signature}
last_error = None
for attempt in range(3): # bounded retry
try:
resp = requests.post(webhook_url, data=body, headers=headers, timeout=10)
if resp.status_code == 200:
return True
last_error = f"HTTP {resp.status_code}"
except Exception:
last_error = frappe.get_traceback()
frappe.log_error(last_error, f"Webhook failed: {event} -> {webhook_url}")
return False
Refer to the background-jobs reference for the full set of frappe.enqueue options (job_id, deduplicate, enqueue_after_commit, queues, timeouts).
Frappe Webhook Implementation: