How ERPNext webhooks push document events to other systems
Frappe webhooks are configured under Integrations > Webhook by selecting a DocType, a document event such as on_update, optional conditions, and the URI that receives an HTTP request when the event occurs. A team that needs a downstream system, such as a warehouse tool, a reporting store or a messaging channel, to react when a Sales Order, Delivery Note or Payment Entry changes can often meet that need with this built-in mechanism before writing any code. The webhook fires after ERPNext has saved the document, so the outbound call reflects a committed change in ERPNext, which remains the system of record.
The method covers outbound notifications of single document events. It does not give you a reliable transactional outbox, guaranteed ordering across documents, retries you control, or inbound writes back into ERPNext. Before committing, confirm with your Frappe administrator which webhook fields and events your ERPNext version exposes, how failed deliveries are logged and retried in that version, and whether the receiving system can handle duplicate or out-of-order requests. Also check whether the condition you need can be expressed as a simple field check or requires a Server Script, which is disabled by default from version 15 and not available on Frappe Cloud public shared benches.
Use a webhook when the receiver only needs to know that something happened and can fetch the current state through the ERPNext REST API, and when the volume of events is modest and failures can be investigated by hand. Plan a custom app instead when you need idempotent delivery with a queue, field-level payload transformation across several DocTypes, correlation with external state, or audited retries that your operations team must answer for. Write down which of these conditions applies before you build, because moving from a webhook to a custom app later changes the receiver contract, the monitoring and the on-call burden.
What you need before configuring an ERPNext webhook
A webhook is only as dependable as the environment around it. Confirm the ERPNext and Frappe version, the receiving endpoint, the network path from your ERPNext server or Frappe Cloud site, and the user who will create the webhook before you touch the configuration. Each card below covers one of these checks.
Confirm a supported ERPNext version
Check that your site runs a version still listed on Frappe's supported versions page. Version 14 reached end of life on January 31, 2026, so a webhook built on it will sit on an unsupported base. Frappe's policy also ties warranty coverage to a listed version and a matching Frappe Cloud support plan. Record the exact version of ERPNext and Frappe Framework on each site, since webhook options and behavior can differ between major versions.
Confirm webhook behavior for your version
Webhook fields, available document events and the request payload are documented per release. Before building anything, read the Webhook documentation that matches your Frappe version and test one event on a non-production site. Do not assume a field name or event carried over from an older release or from a blog post.
Prepare the receiving endpoint
The receiver must be reachable over HTTPS with a certificate that clients trust, and it should answer quickly with a success status so ERPNext does not treat the delivery as failed. Confirm the owner of the endpoint, its uptime expectations, and how it handles retries or duplicate deliveries. Decide whether the endpoint will accept requests only from known sources, and how it will verify them before processing.
Check network reachability
Confirm the outbound path from your ERPNext server, or from the Frappe Cloud site, to the receiver's address and port. Firewalls, egress proxies, private DNS and allow lists often block webhooks silently in self-hosted environments. On Frappe Cloud, confirm the destination accepts traffic from the platform's outbound addresses, and test with a harmless document change before relying on the path.
Assign a role that can create webhooks
Creating a webhook is an administrative action in ERPNext. Confirm which role your team uses to open Integrations, Webhook, and that the person configuring it holds that role on the correct site. Keep webhook configuration with a small group of administrators, and record who changed each webhook, because a single edit can send sensitive document data to a new destination.
Plan for sensitive document data
A webhook sends the full document content for the selected DocType and event to the URI you configure, so customer, pricing and payment fields may leave ERPNext. Before choosing a DocType, list which fields the receiver should see and confirm that the destination is approved for that data under your data handling rules. This check belongs before configuration, not after the first delivery.
Configuring a webhook in sequence
This sequence takes you from an idea about a document event to a working webhook that sends a request to your receiving system, with a check at each stage so you know the event fired and the payload arrived. It is written for the ERPNext administrator and the engineer who owns the receiving endpoint, working on a non-production site first.
Confirm the receiver and the event you need
Before opening ERPNext, agree with the receiving team which business event should trigger a request, which DocType holds that record, and what the receiver will do with it. Write down the endpoint URL, the authentication the receiver expects, and whether it needs the full document or only identifiers. Confirm these on a staging or test site, not production. Done when both teams have a one-page note naming the DocType, the event and the receiver's required headers.
Create the webhook under Integrations
As a user with System Manager or an equivalent role, go to Integrations > Webhook and create a new record. Select the DocType whose documents should trigger the request, such as Sales Order or Sales Invoice, and choose the document event, for example on_update, on_submit or after_insert, that matches the business moment you agreed on. Save the record in draft form so you can finish the remaining fields. Done when the record exists and shows the DocType and event you selected.
Add conditions to limit which documents fire
Add a condition expression so only relevant documents trigger the webhook, for example a status or company filter, instead of every save. Test the expression against a small set of known records on the test site. Keep conditions simple because a broad or wrong condition sends traffic for documents nobody meant to publish. Done when a document that meets the condition is sent and one that does not meet it produces no request in the receiver's logs.
Set the request URL, method and headers
Enter the receiver's URL in the request URL field and choose the HTTP method the receiver expects. Add any required headers, such as an authorization token or content type, in the webhook's request headers section. Review the request body template so that only the fields the receiver needs are sent, and avoid placing secrets in fields visible to users who lack access to the record. Done when the URL, method and headers match the note from the first step.
Save and enable the webhook
Save the webhook and confirm it is enabled. Check that the record appears in the list of webhooks for the site and that the DocType, event, condition and URL are exactly as intended. Keep a copy of the configuration in your change record so the next administrator can see what was set and why. Done when the saved record matches your written configuration and the change is logged.
Trigger a test change on a non-production record
On the test site, edit a single record that meets the condition, such as a draft document you created for testing, so that the chosen event fires. Do not use live customer or accounting data for this test. Watch the receiver's logs or endpoint for the incoming request. Done when the receiver shows one request with the expected method, headers and body, and a document that does not meet the condition produces no request.
Check the payload and failure handling
Compare the received payload field by field with the structure agreed in the first step and confirm that the receiver returns a success response. Then test a failure: make the receiver return an error or stop responding and confirm how the receiver handles retries and what ERPNext records for the failed attempt. Ask your ERPNext partner or the Frappe documentation for the current retry and error log behavior for your version. Done when the success and failure paths both behave as the receiver's team expects.
Promote to production and monitor the first changes
Repeat the configuration on the production site only after the test checks pass, and make the change in a planned window agreed with the receiving team. Watch the first real events closely, comparing volume and content with the test results. Document who owns the webhook, where its errors appear and what triggers a review. Done when the first production events arrive as expected and the ownership and monitoring notes are stored with the configuration.
Choosing the DocType and document event that fits the business event
Start by naming the business event in plain terms, then find the DocType that records it. A Sales Order submitted to confirm a customer commitment is a different event from a Sales Order saved as a draft, and a Payment Entry created when cash lands is different from a Sales Invoice created when revenue is recognized. In Frappe, a webhook is set up under Integrations > Webhook by selecting one DocType and one document event, such as on_update, then adding optional conditions and the receiving URI. Each choice determines what the receiving system hears about and when. Choose the DocType that holds the authoritative record for the event, not a related document that is only updated as a side effect. Write the mapping down before building anything: business event, DocType, event, condition, receiving system, and the field it needs to act on.
The event choice drives duplicate and ordering risk more than any other setting. on_update runs whenever a document is saved, so one business action, such as a user submitting a document and a later background job updating a status field, can send several requests for the same record. The receiving system must therefore be idempotent, keyed on the document name and a stable identifier, and must not assume that one webhook equals one business event. Requests may also arrive out of order when saves happen close together. Conditions help by filtering on a field value, for example only when a status changes to a specific value, but they do not remove the need for deduplication. Check the event list and condition behavior for your ERPNext and Frappe version in the Frappe webhooks documentation before you commit to an event, and test each event with a sandbox receiver that logs every call.
Filtering conditions and request structure
Conditions restrict a webhook so it fires only for documents that match a rule. In Frappe, the condition is evaluated against the document at the moment the selected event occurs, so a condition on an on_update event sees the saved state of the record. Conditions are the main control against sending every change to an external system, which matters most for DocTypes that change often, such as Sales Invoice or Stock Ledger-related records, where an unfiltered webhook can generate large volumes of requests. Write the condition as a simple expression on fields the document actually holds, and confirm the field names in your version before relying on them.
- Limit by status: Fire only when a document reaches a state the receiver needs, such as a submitted invoice rather than a draft.
- Limit by company or territory: Route records to the right receiver when one site serves several entities.
- Avoid conditions on fields that change during save: A field updated later in the same transaction can produce unexpected results.
- Document each condition: Record the business reason next to the webhook so later editors understand why it exists.
- Plan for missed events: A condition that is too strict silently drops records, so reconcile counts periodically.
Before enabling the webhook, check each condition against a few sample documents in a test site, including one that should match and one that should not.
Testing conditions on sample documents is cheaper than discovering a mismatch in production. Use a non-production site with copies of representative records, or create documents that deliberately match and miss each rule. Save and submit them through the same path users take, because some events depend on whether a document was saved, submitted or cancelled. Compare the number of requests your receiver logs against the number of documents you expected to match, and treat any difference as a defect until it is explained.
- Build matching and non-matching pairs: Each condition should have at least one record that fires and one that does not.
- Cover edge cases: Test cancelled documents, amended documents and records with empty optional fields.
- Use a logging receiver: A temporary endpoint that records headers and bodies shows exactly what fired.
- Test bulk actions separately: Imports and mass updates can trigger events differently from single-document saves.
- Retest after changes: Any change to a condition, DocType customization or Frappe version needs a fresh pass.
Keep the test records and results with the change record, so the same checks can be rerun during upgrades.
The receiver depends on the request structure: the HTTP method, headers and the JSON body that Frappe builds from the document. Frappe sends the document data when the event occurs, and the receiver should read only the fields it needs rather than assuming a fixed shape. Headers matter for authentication and routing. Decide how the receiver will verify that a request came from your ERPNext site, and treat any shared secret as a credential to be stored and rotated under your normal secret management practice. Do not assume the body is identical across Frappe versions.
- Confirm the method and URI: Check that the endpoint accepts the method Frappe uses and that it is reachable from the ERPNext host.
- List required fields: Write down each field name the receiver reads, including nested child table rows.
- Handle optional fields: Empty or null values are common in ERPNext, so the receiver should not fail on them.
- Verify authentication headers: Confirm how the receiver checks the request and where the shared value is configured.
- Capture a real payload: Inspect one live request from the test site instead of relying on examples.
Before go-live, verify the exact payload format and headers in the Frappe Framework webhook documentation for your version, and confirm that the receiver parses a captured sample without errors.
Confirming that the payload contains every field the receiver needs is the last check before relying on a webhook. Missing fields usually come from a field left empty on the document, a child table that was not included in the request, or a customization that renamed a field. Compare the captured payload against the receiver's contract field by field, and record which fields are mandatory for processing and which are only informative. This comparison also helps the receiver team decide whether to fetch extra data through the ERPNext API instead of expecting everything in the webhook.
- Map each field to a consumer: Every field in the contract should name the process that depends on it.
- Check child tables: Confirm that line items arrive complete, not only the parent document.
- Test with an incomplete document: Save a record with optional fields blank to see how the receiver behaves.
- Watch custom fields: Fields added by your team may not exist on another site or after an upgrade.
- Agree on a change process: Any new field the receiver needs should go through the same test cycle.
Re-run the field comparison after every Frappe or ERPNext upgrade, and confirm the result against the Frappe documentation for the version you run.
Mapping ERPNext fields to the receiving system's data model
This table compares common ERPNext DocTypes with the fields a receiving system typically needs, showing the transformation and the owner for each. It is illustrative, so confirm every field name against your ERPNext version and your receiver's API before building. The main takeaway is that identifiers and status values cause most mapping failures, so agree on them first.
| ERPNext field | Receiver field (illustrative) | Transformation needed | Owner |
|---|---|---|---|
| Sales Order: name, customer, grand_total, status | external_order_id, customer_ref, total_amount, order_state | Send the name as an external reference, convert currency to receiver precision, map status values to receiver states | Integration engineer with order operations |
| Sales Invoice: name, posting_date, outstanding_amount, is_return | invoice_number, issued_on, balance_due, credit_flag | Keep the invoice name as the key, convert return invoices to a credit type, send balance from outstanding_amount | Finance systems lead |
| Item: item_code, item_name, item_group, stock_uom | sku, product_name, category, unit | Use item_code as the SKU, map item_group to receiver categories, convert UOM where units differ | Product data owner |
| Customer: name, customer_name, tax_id, default_currency | customer_id, display_name, tax_number, currency | Store the ERPNext name as the stable customer key, validate tax_id format per country, default currency where blank | Master data owner |
| Stock Entry: name, stock_entry_type, items (child rows), posting_date | movement_id, movement_type, lines[], effective_at | Explode child rows into one line per item, map stock_entry_type codes to receiver movement types, use posting_date with timezone | Warehouse operations lead |
| Document naming series and status fields (docstatus, status) | Receiver idempotency key and state | Combine DocType and name into one key, map docstatus 0, 1 and 2 to draft, submitted and cancelled, never reuse a cancelled name | Integration engineer |
When a webhook is enough and when a custom app is the better route
A webhook suits a one-way push of document state to a receiver you already control. Once the integration needs guaranteed delivery, richer logic, writes back into ERPNext or code that is reviewed and versioned, the work belongs in a custom app. The six points below help the team draw that line before the build starts.
Simple push of document state
If the receiver only needs to know that a Sales Order was submitted or an Item changed, a webhook posting the document fields to a URI is usually enough. Confirm that the receiver can read the fields you need from the request body without a second lookup back into ERPNext. If the receiver must call ERPNext again for every event, the integration is already more than a push and should be scoped as a custom app.
Retries and idempotency
Webhooks fire once per matching event and do not give you a built-in dead-letter queue or replay of failed deliveries. If a lost or duplicated message would cause a double charge, a duplicate shipment or a double ledger entry, the receiver needs its own idempotency keys and a reconciliation job, or the team should move to a custom app that owns the queue. Ask your team which failures are acceptable and what a missed event costs.
Transformation beyond field mapping
A webhook sends the document as Frappe builds it. Tax splits, currency conversion, grouping several lines into one external order or looking up codes in another system all push work outside the webhook configuration. When the mapping needs branching, lookups or calculations, plan a custom app with tests, or a middleware service you operate, rather than stacking logic into the webhook body.
Inbound writes back to ERPNext
Webhooks are outbound only. When the external system must create or update ERPNext records, such as stock adjustments from a warehouse system or payment confirmations from a gateway, the team needs an authenticated API endpoint or a custom app, with validation, permissions and error handling. Decide early which system is the source of truth for each field so that two systems never overwrite the same record.
Event volume and timing
High-frequency documents, such as stock ledger changes or bulk imports, can generate many requests in a short period, and the receiver must absorb them. Check the expected event count per hour with your own data, test under a bulk import, and decide whether a queue in front of the receiver is needed. Document the ordering assumption too, because webhook calls for two updates to the same record may arrive out of sequence.
Code review and versioning
Webhook configurations live in the site database, so changes made in the Desk are not reviewed or versioned alongside code. Server Scripts, which can run Python on document events, are disabled by default from version 15, must be enabled with the server_script_enabled setting, and are not available on Frappe Cloud public shared benches. Teams that need peer review, tests and release tags should package the logic as a custom app and keep the webhook configuration documented in the same repository.
Why webhook deliveries fail or arrive twice
This table maps the symptoms teams usually see with ERPNext webhooks to their likely causes and the first fix to try. Read it from the symptom down, and confirm each cause against the webhook's log and the receiving system's logs before changing configuration. The main takeaway is that most failures trace back to the event choice, the condition, or the receiver's response time, not to ERPNext itself.
| Symptom | Likely cause | Fix |
|---|---|---|
| No request is sent at all | The condition on the Webhook never evaluates to true for the saved document | Open the document in its current state and test the condition against its field values, then simplify the condition and save a test record |
| Request fires on save when you expected a submit | The chosen document event is on_update, which runs on every save, not on submission | Review the event selected for the DocType and switch to the event that matches the business step, then retest with a draft and a submitted document |
| Same event arrives two or three times | Several saves or updates each fire the event, and the receiver treats each one as new | Include the document name and modified timestamp in the payload, and make the receiver idempotent so repeated deliveries do not create duplicate records |
| Receiver returns errors and nothing is retried in your system | The receiving endpoint rejects the request format, authentication header or content type | Check the response code and body in the Webhook log, match the expected request structure, and return a success code only after the receiver has accepted the data |
| Requests time out during busy saves | The receiver does slow processing inline before it responds to ERPNext | Acknowledge the request quickly, queue the work on the receiver side, and process the queued job asynchronously |
| TLS or certificate errors on the URI | The receiver uses an expired, self-signed or incomplete certificate chain | Install a valid certificate chain on the receiver and test the URI from the ERPNext server, not only from a browser |
| Fields missing from the payload | The payload template omits fields, or the field is empty on the document at the time of the event | Review the request body template against the DocType fields, add the missing fields, and confirm which values are populated before the chosen event fires |
Questions buyers ask about ERPNext webhooks
What drives the cost of an ERPNext webhook project?
The cost follows the number of events, the complexity of the receiving system, and the amount of testing needed. Each DocType and document event you push adds configuration, payload mapping and verification work. A receiver that only accepts a simple JSON post is far cheaper to build than one that must validate signatures, transform data, retry safely and write back to ERPNext. Testing usually takes a large share of effort because every condition and failure path needs a repeatable check. Ask for a written scope that lists each event, each receiver and each test case, since that list is what a fixed quote is built from. Hosting and licensing for ERPNext itself are separate from this work, and the ERPNext pricing page states that self-hosting is free.
Can our team set up an ERPNext webhook without a partner?
Yes, for a simple case. Frappe webhooks are configured under Integrations > Webhook by choosing a DocType, a document event such as on_update, optional conditions and the URI that receives the HTTP request. A team with an ERPNext administrator and one engineer who can build the receiving endpoint can handle a single event to a single receiver. Bring in a partner when you need custom payload logic, several receivers, signature checks, retry handling across failures, or changes that must be reviewed and released like application code. Confirm that your team can keep the webhook running after the person who built it leaves.
What does our team need to provide for a webhook build?
Three roles matter most. An API owner on the receiving side must define the endpoint, the expected payload, authentication and error responses. A test environment with a non-production ERPNext site lets the team trigger events safely, since webhooks fire on real document changes. A sign-off owner from finance or operations must confirm which documents and statuses should leave ERPNext and how the receiver should treat duplicates. Also provide access to the receiving system's logs so failures can be traced. Name these people in the written scope before work starts.
How long does a scoped ERPNext webhook build take?
Duration depends on scope, and no fixed figure applies across projects. A single event to one receiver with a standard payload is a short build. Adding conditions, payload transformation, a receiver with retry and idempotency handling, and a test plan extends the timeline. Several DocTypes, multiple receivers or a receiver that writes back into ERPNext extend it further. Ask the partner for a timeline tied to the written deliverables and to the test cycles you will approve. Confirm the dependencies on your side, such as endpoint availability and sign-off dates, because they often set the pace.
When is a webhook enough, and when do we need a custom app?
A webhook is enough when you only need to notify another system that a document changed and the receiver can handle the payload as sent. Choose a custom Frappe app when the logic must run inside ERPNext, such as validating data before save, creating linked documents, calling external APIs with business rules, or handling large volumes that need controlled background processing. Server Scripts can run Python on document events, but from version 15 they are disabled by default and must be enabled with the server_script_enabled setting, and Frappe Cloud public shared benches do not allow them. Check that setting and your hosting model before you choose this path.
What are the main risks with ERPNext webhooks?
The most common risks are duplicate deliveries, lost events during receiver outages, payloads that expose more data than the receiver should hold, and changes to document structure that break the receiver after an upgrade. Plan idempotent handling on the receiver, a retry or reconciliation job for missed events, and an explicit field list rather than full document dumps. Treat the endpoint as a trust boundary: restrict it, authenticate requests and log failures. Test each ERPNext version upgrade against your webhook payloads before go-live on production.
Does DeployOpen set up ERPNext webhooks, and how is the work scoped?
DeployOpen plans, implements, customizes, integrates, documents and supports self-hosted open-source platforms in your own infrastructure, and ERPNext webhook work falls within that scope. Most engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts. Ongoing support, upgrades and testing can run as retainers. DeployOpen does not publish a price list, so a quote follows from the written scope. Ask for the event list, receiver specifications, test cases, sign-off roles and the upgrade assumptions to be written into that scope.
Operating webhooks after go-live
The platform team that owns a webhook needs to know, at any moment, whether ERPNext is still delivering events and whether the receiving system is still accepting them. A webhook that fails quietly is the most common operational gap, because ERPNext records the document change regardless of whether the outbound request succeeded. Build a simple monitoring routine around three signals: the receiving system's own request logs, error entries on the ERPNext side, and a periodic reconciliation that compares a sample of recent documents against what the downstream system holds. Confirm in your ERPNext version where failed webhook attempts are logged and how long those logs are retained, and decide who is alerted and how quickly. Also agree on how a missed event is replayed, either by re-saving or re-sending the document or by a scheduled backfill query, and test that path before go-live rather than during an incident.
Change control matters because webhook payloads are built from DocType fields. When a later ERPNext release renames, removes or changes the behavior of a field, a webhook that worked before can send incomplete data without raising an error. Keep a register that maps each webhook to its DocType, document event, conditions and the exact fields in its request body, and treat any custom field you added to that DocType as part of the integration contract. Review the register whenever you plan a version change, and make sure any Condition expression or Jinja template in the webhook is rechecked against the new release. Confirm with your partner or Frappe's release notes which DocTypes and fields changed before you approve the upgrade.
Testing after each upgrade should replay a fixed set of scenarios against a non-production site with the same app versions and customizations as production. Include a create, an update that matches the condition, an update that does not match it, and a cancellation or deletion where relevant, and compare the received payloads with the stored expectations field by field. Check that signatures or authentication headers still validate, that the receiving endpoint still returns success codes, and that no duplicate records appear downstream. Document which events feed which system in a single map that the platform team maintains alongside the webhook register, so that anyone reviewing an upgrade can see the full blast radius of a change without reading each webhook configuration.
How DeployOpen scopes and delivers ERPNext webhook integrations
DeployOpen plans, builds, tests and hands over ERPNext webhook integrations inside your own infrastructure, starting from a written scope. We decide with you which document events should leave ERPNext, which receiving system needs them, and where a custom app is the better home for the logic.
How we scope
- We list each DocType and document event that must notify another system, such as on_update or on_submit.
- We define the conditions that limit which documents fire, and confirm each receiving endpoint and its expected payload.
- We agree a fixed scope in writing, covering deliverables, testing evidence and handover documents before work starts.
How we build
- We configure webhooks under Integrations, then test each one against a staging endpoint before any production use.
- We confirm whether Server Script logic is needed, checking that your version and hosting allow it before we rely on it.
- We move logic into a custom app when retries, transformations or ordering requirements exceed what a webhook can express.
How we hand over
- We document each webhook's DocType, event, conditions, URI and request structure in a handover pack your team can maintain.
- We walk your administrators through testing, disabling and rotating receiving endpoints, and reviewing delivery logs.
- We record the version, hosting model and dependencies the integration relies on so upgrades can be planned against them.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- We can review webhook changes during ERPNext minor and major upgrades, where the receiving systems are part of your scope.
- We confirm with you which operational owner handles failed deliveries, endpoint changes and access reviews after go-live.

