How EspoCRM webhooks push record changes to other systems
A team that needs CRM changes to reach an ERP, a data warehouse, a messaging tool or a custom service should not build a polling job against the EspoCRM API. EspoCRM webhooks are the supported outbound method, and you configure them through the REST API using a dedicated API user rather than a personal login. Each webhook subscribes to an event on a chosen entity type and sends an HTTP request to a URL you control when that event happens. The sending side is EspoCRM; the receiving endpoint, its parsing, its storage and its downstream logic must be built by your team or a partner. Nothing in the product provides a ready-made consumer for your ERP or warehouse.
The event types differ in what they carry, and your receiver should be designed around that. A create event sends all attributes of the record. An update event sends only the attributes that changed, so a receiver that keeps a copy of each record must merge partial payloads instead of overwriting them. A delete event identifies the removed record. For narrower triggers, ENTITY_TYPE.fieldUpdate.FIELD fires only when a specific field changes, for example Account.fieldUpdate.assignedUserId when account ownership moves. Subscribing to field-level events rather than all updates reduces traffic and the processing your downstream system must handle. The webhook owner's own changes can also echo back into the subscription. From version 9.2, the skipOwn parameter skips events that the webhook owner initiated, which matters when the receiver writes back to EspoCRM. Confirm the exact payload shape for your version in the webhooks documentation before you write the parser.
Before you go live, confirm the API user's permissions. According to the EspoCRM webhooks documentation, the API user needs the Webhooks scope and access to every entity type it subscribes to, so a missing entity permission will block a subscription you expected to work. Decide who may create webhooks and keep that API user separate from people and other integrations. The receiver should verify the signature with the secret key before it trusts a payload, and your team should also decide how failed deliveries are handled, how duplicates are recognized, and how events are replayed from your own logs, since you cannot assume delivery is exactly once. Ask your implementation partner to document these decisions in the statement of work so that the receiver, the API user and the monitoring are designed together.
What you need before creating the first EspoCRM webhook
A webhook is only as reliable as the instance, identity and endpoint behind it. The cards below cover the EspoCRM version, the hosting model, the API user, the receiving endpoint and the secret store, which together decide whether the first event arrives and whether it can be trusted.
Confirm the EspoCRM version
Run the 10.0.x line, which is the current major series on the releases page. The skipOwn parameter needs version 9.2 or later, so confirm the exact build in Administration before you design subscriptions around it. Check the release notes for your build because delivery behavior and header details can change between versions, and confirm them in the webhook documentation for that version before you write any receiver.
Pick the hosting model
Decide whether the instance is self-hosted in your own infrastructure or runs on EspoCRM Cloud. Either way, the receiving system must be able to reach the instance, and the instance must be able to reach the receiver over HTTPS. Confirm which outbound network paths, firewall rules and proxy settings apply, since a webhook sent from behind a restricted network can fail silently.
Create a dedicated API user
Webhooks are configured under an API user, not a person's login. That user needs the Webhooks scope and read access to every entity type it subscribes to. Create a separate user for each integration so that permissions, rotation and audit trails stay isolated. Grant the minimum access needed, and record which entity types the integration depends on so access reviews stay accurate.
Prepare the receiving endpoint
The receiver needs a publicly reachable HTTPS endpoint with a valid TLS certificate, a route that accepts POST requests, and logging for every inbound delivery. Plan for duplicate and out-of-order deliveries by making the handler idempotent, keyed on the record ID and the event time. Test it with a non-production instance before pointing production events at it.
Choose the signing key store
Webhook signature checking depends on a secret that both EspoCRM and the receiver hold. Decide where that secret lives before creation, such as a secrets manager your team already operates, and who may read or rotate it. Keep it out of source control and out of ticket comments, and document the rotation procedure, because replacing the key later requires coordinated changes on both sides.
Plan the event and retry handling
Before creating the first webhook, decide how the receiver responds to failed deliveries and how the team learns about them. Define an alert threshold, a dead-letter location for payloads that cannot be processed, and an owner for each subscription. Confirm the retry behavior in the documentation for your version, since EspoCRM's exact retry handling is not described in the topic facts and should be verified in your own tests.
Configure a webhook subscription step by step
This sequence takes a team from a locked-down API user to a verified, signed webhook that fires on the right record changes and nothing else. Each step ends with a check that proves the permission boundary and payload shape work before any production system relies on them.
Create a dedicated API user
An administrator creates a separate API user in EspoCRM for the webhook integration, rather than reusing a person's account or an admin account. Keep it distinct from users who work in the interface so its activity is traceable in logs and its access can be changed without affecting staff. Record who owns the user and which downstream system depends on it. The step is done when the user exists, is active, and is not a member of any team or role that grants broader access than the integration needs.
Limit the role to subscribed entities
Build a role for the API user that grants read access only to the entity types the webhook will subscribe to, such as Account or Opportunity, and nothing else. The EspoCRM webhooks documentation requires access to every entity type a subscription covers, so a missing permission will block events for that entity. Set record scope (own, team, or all) deliberately, since it determines which records generate events for this user. The check is a test request made as the API user against an entity outside the role, which should be rejected.
Grant the Webhooks scope
Add the Webhooks scope to the same role, because the API user cannot create subscriptions without it. Keep the scope limited to the integration's role so that no interactive user can manage webhooks by accident. Confirm the scope is saved by attempting a webhook request as the API user. If the request is accepted, the scope works; if it returns an authorization error, the role is missing the scope or the entity access from the previous step.
Choose HMAC authentication for the API user
Configure the API user with HMAC authentication, which the EspoCRM REST API documentation describes as the most secure of its supported methods. Basic authentication is documented as not recommended, so avoid it for any integration that writes or subscribes to events. Store the API key and secret in your secrets manager and restrict the user's access by IP address where your network allows. The check is a signed test request from the integration host that succeeds, followed by the same request with a wrong secret, which must fail.
Create the webhook through the API
Send a request as the API user to create the webhook, specifying the event name, such as Account.create or Account.update, and the target URL of the receiving system. The target should be an HTTPS endpoint you control, and the response returns the webhook record, including the secret key used to verify signatures. Store that secret in the receiving system before going further. The check is that the webhook appears in the list of webhooks with the expected event and URL, and that it is active.
Fire a test event and inspect the payload
Make a change to a non-production record, such as creating a test account and then updating it, and confirm the receiving endpoint logs a delivery. Compare the two payloads: according to the EspoCRM webhooks documentation, a create event sends all attributes, while an update event sends only the attributes that changed. Verify the receiving system checks the signature before it reads the body. The step closes when one create and one update delivery are logged with the payload shapes you expected.
Add field-level subscriptions last
Once entity-level events are verified, add field-level subscriptions in the form ENTITY_TYPE.fieldUpdate.FIELD, such as Account.fieldUpdate.assignedUserId, so the receiver is notified only when a specific field changes. Adding them last keeps any fault in the earlier steps easy to isolate. Test each field-level event with a change to that field and a change to another field, which should not trigger a delivery. Ask your EspoCRM administrator to confirm the event names against the version you run, since event support depends on the release.
Mapping events, payloads and API user permissions
This table compares the webhook event types EspoCRM can send, what each one carries, the names a receiver must handle, and the API user access each one needs. Read each row as a checklist for both the EspoCRM configuration and the receiving system's parser. The main takeaway is that a missing entity permission on the API user will silently stop a subscription from delivering the events you expect.
| Event type | What the payload contains | Names the receiver must expect | API user access required |
|---|---|---|---|
| Create | All attributes of the new record | Entity type name and every attribute of that entity, including empty fields | Webhooks scope plus read access to the subscribed entity type |
| Update | Only the attributes that changed | Entity type name and changed attribute names only, so missing fields mean unchanged | Webhooks scope plus read access to the subscribed entity type |
| Delete | Identifiers of the removed record | Entity type name and record identifier; confirm the exact fields in a test in your version | Webhooks scope plus access to the subscribed entity type |
| Field-level update | Triggered when one named field changes | Event name in the form ENTITY_TYPE.fieldUpdate.FIELD, for example Account.fieldUpdate.assignedUserId | Webhooks scope plus access to the entity type that owns the field |
| Loop prevention (skipOwn) | Suppresses events the webhook owner initiated | No change to payload; the receiver should still guard against repeat writes | Available from version 9.2; confirm the version in your instance before relying on it |
| Entity without access | No events delivered for that entity | Subscription may appear configured but produce no deliveries | Read access to every entity type the subscription uses, checked for the API user's role |
Choosing between entity events and field-level events
Entity-level subscriptions fit audit trails, search indexes and full synchronization to a downstream store, where the receiver needs to see every create, update and delete on an entity type. A create event sends all attributes of the record, which gives the receiver a complete starting copy. An update event sends only the attributes that changed, so the receiver must keep its own copy current and apply partial changes correctly. Broad subscriptions are the simplest to reason about because one rule covers the whole entity type.
- Complete baseline: Create events carry the full record, so a new consumer can seed itself without a separate export.
- Partial updates: Update events carry only changed attributes, so merge logic must not overwrite fields that were absent from the payload.
- Delete handling: Subscribe to delete events explicitly if the downstream copy must be removed or archived.
- Permission coverage: The API user needs the Webhooks scope and access to every entity type it subscribes to, so a broad subscription also widens the user's role.
- Volume: Busy entities such as activity or email records generate far more events than master records.
Before you choose entity-level subscriptions, confirm with your team which entity types truly need mirroring and whether the receiver can handle the peak event volume.
Field-level subscriptions use the ENTITY_TYPE.fieldUpdate.FIELD pattern, so a webhook fires only when a specific field changes. This suits narrow triggers such as routing a lead when its assigned user changes, or notifying a service desk when an account owner is reassigned. Because the receiver is not told about every edit, it processes far fewer payloads and its logic stays focused on one business event.
- Narrow triggers: Account.fieldUpdate.assignedUserId fires only when the assignee changes, not on every account edit.
- Lower receiver load: Fewer deliveries reduce processing, retry and log noise on the receiving side.
- Field naming: Confirm the exact field names in your instance, since custom fields follow your own naming in the Entity Manager.
- Gaps in coverage: A field-level subscription will not tell the receiver about changes to other fields, so combine it with an entity subscription where a full picture is needed.
- Reassignment logic: Decide whether the receiver should act on the new owner only or also needs the previous owner, which the event may not supply.
Check that each field you subscribe to is the one your business rule depends on, and test a reassignment end to end in a non-production instance.
Loops appear when a receiver writes back to EspoCRM. For example, an integration that receives an assignment change and then updates the same record can trigger its own webhook again. The skipOwn parameter, available as of version 9.2, tells EspoCRM to skip events the webhook owner initiated. Using it keeps the write-back path from re-entering the integration, although it does not replace idempotent processing on the receiver side.
- Write-back loops: Without skipOwn, a receiver that updates records can process its own changes repeatedly.
- Version dependency: skipOwn requires version 9.2 or later, so confirm your instance version before relying on it.
- Multiple writers: Changes made by other API users or people are still delivered, so the receiver must handle them.
- Idempotency: Retries and duplicate deliveries can still happen, so the receiver should tolerate repeated events.
- Testing: Verify that a write-back produces no second delivery and that a human edit still does.
Confirm the behavior of skipOwn against the webhooks documentation for your exact version before you rely on it in production.
Noisy subscriptions cost money and attention in two places. On the receiver side, every delivery needs parsing, validation, storage and possibly a downstream call, so a broad subscription on a busy entity can overwhelm a queue or an external API quota. On the EspoCRM host, each event leads to outbound requests and related background processing that share the same server, database and scheduled job capacity as users. Both costs recur every day the subscription is active, so they grow with usage rather than with the initial build.
- Receiver effort: Each extra payload adds parsing, error handling and monitoring that someone must maintain.
- Host load: Outbound deliveries share PHP workers, database capacity and background job processing with interactive users.
- Failure amplification: A slow or failing receiver can cause retries that accumulate on the host.
- Recurring cost: Hosting, queue capacity and third-party API charges scale with event volume, not with the one-time build.
- Capacity planning: Ask your EspoCRM administrator to size the host against measured event rates from a pilot.
Before approving a broad subscription, ask for an estimate of daily event volume per entity type and a measured load test on your staging environment.
Verifying signatures and hardening the receiver
A webhook receiver is an internet-facing endpoint that accepts changes from your CRM, so it needs to prove each request is genuine before it acts on it. The cards below cover signature checking, freshness, network limits, the API user that sends events, and where the secret lives. Together they decide whether a forged or replayed request can change your downstream systems.
Verify the signature before trusting the payload
Compute the expected signature over the raw request body using the webhook secret key, then compare it with the signature header EspoCRM sends. Reject any request that fails this check before parsing the body or writing anything. Confirm the exact header name and algorithm in the webhooks documentation for your EspoCRM version, since your receiver must match them exactly. Use a constant-time comparison so the check does not leak timing information.
Reject unsigned and stale requests
Treat any request without a signature, with a malformed signature, or with an event timestamp outside your accepted window as invalid. Stale requests are the usual route for replaying a captured valid payload later. Ask the vendor or your own tests whether timestamps are included in the signed content and how your receiver should handle clock skew between servers. Log rejections with source address and event type so repeated failures are visible.
Restrict the endpoint by network origin
Where your network design allows it, accept webhook traffic only from the EspoCRM server's outbound address or from a dedicated egress range. Enforce this at the reverse proxy or firewall, not only in application code. IP restriction is a second layer, not a replacement for signature checks, because addresses can change when EspoCRM moves hosts or sits behind a NAT. Confirm the source addresses with your team before you apply the rule.
Authenticate the API user with HMAC
The API user that EspoCRM uses to manage webhook subscriptions and read records should authenticate with HMAC, which the EspoCRM API docs describe as the most secure option among the supported methods. Basic authentication is not recommended. Keep the API user's role limited to the Webhooks scope and the entity types it actually needs, so a compromised key cannot read or change data beyond that scope.
Keep admin accounts out of integrations
Do not create webhook subscriptions or API credentials under a administrator account used for daily work. Use a dedicated, non-interactive API user with its own role, and review its permissions whenever a new subscription is added. The EspoCRM security guidance also recommends avoiding admin accounts for daily work and limiting token lifetime, so apply the same discipline to integration identities. Document who owns each API user and which systems depend on it.
Store the secret outside the configuration files
Keep the webhook secret key in a secrets manager or a restricted environment variable on the receiver side, not in a file that is committed to version control or copied into shared runbooks. Rotate the secret on a schedule you define and after any staff change, then confirm the receiver accepts the new value before retiring the old one. Restrict read access to the receiver's service account and to the people who operate it.
Why webhook deliveries fail or go missing
This table lists the failure patterns that most often stop EspoCRM webhooks from reaching a receiver, with the cause and fix for each. Read it before go-live and again when a receiving system reports missing or duplicate events. Confirm retry behavior in the EspoCRM webhooks documentation for your installed version, because retry handling is not something to assume.
| Failure | Likely cause | Fix |
|---|---|---|
| Webhook never fires | The API user lacks the Webhooks scope in its role | Grant the Webhooks scope to the API user, then create a test record and confirm the event arrives |
| Some events silently skipped | The API user cannot access every entity type it subscribes to | Give the API user read access to each subscribed entity type, and test with a record the user can and cannot see |
| Field-level event missing | The subscription uses the wrong ENTITY_TYPE.fieldUpdate.FIELD name or the field did not actually change | Check the exact entity and field name, and change the field value in the test to confirm the event fires |
| Receiver times out | The receiving endpoint responds slowly, so the delivery is treated as failed | Acknowledge quickly, queue the payload for processing, and keep heavy work out of the HTTP handler |
| Signature check fails | The secret key was rotated or copied incorrectly, so the receiver computes a different signature | Update the stored secret on the receiver, verify against the raw request body, and retest after every change |
| Duplicate records downstream | Retries or repeated updates arrive again, and the receiver has no idempotency key | Store processed event identifiers and skip repeats, so each event applies only once |
| Events stop or arrive late | Background jobs are not running because cron or the scheduled task is missing or stalled | Confirm cron runs on the schedule the server configuration requires, and check job logs after each change |
| Own changes echo back | The webhook owner's own updates trigger the subscription again | Use skipOwn where your version supports it (as of version 9.2), and confirm the parameter in the docs for your release |
Questions buyers ask before outsourcing EspoCRM webhook work
What drives the effort of an EspoCRM webhook integration?
Effort rises with four things: how many entity types and field-level events you need, how many receiving systems consume them, how much transformation each receiver needs, and how carefully failures must be handled. A single entity with create and update events sending to one endpoint is a small job. Several entities, field-level triggers on fields such as assigned user, multiple receivers with different payload shapes, and a requirement to retry safely and alert on failures multiply the design, build and test work. Ask the partner to break the estimate down by entity, event type, receiver and error path so you can see which items you can reduce.
Can our team build EspoCRM webhooks without a partner?
Often yes, if you have an engineer comfortable with REST APIs, HTTP signing and queue-based retry logic, and someone who knows the CRM data model well. The EspoCRM webhook docs cover subscription through the API, the difference between create and update payloads, and field-level events such as Account.fieldUpdate.assignedUserId. The harder parts are usually outside the CRM: idempotent receivers, dead-letter handling, monitoring and agreeing on payload contracts with the teams that own each receiving system. If those skills are thin in-house, a fixed-scope engagement can cover design and build while your team takes over operations.
What do we need to provide before the work starts?
Plan to provide a dedicated EspoCRM instance or test environment, an administrator who can create API users and roles, and named owners for each receiving system who can confirm payload needs and test endpoints. You will also need representative test records covering the entities and field changes in scope, plus a decision on where secrets are stored. The API user needs the Webhooks scope and access to every entity type it subscribes to, so the CRM owner must agree on those permissions up front. Missing endpoint owners are the most common cause of delay.
How long does an EspoCRM webhook project usually take?
Duration depends on scope, not on a fixed figure. The main variables are the number of entities and events, how many receivers must be built and tested, how much error handling and monitoring you require, and how quickly your receiving teams can review payloads and test in their own environments. Ask for a timeline in the written scope with milestones for payload design, a first working subscription, receiver testing and cutover. Timelines that do not name the receiving-system owners and their review windows are usually optimistic.
Does DeployOpen issue certifications for webhook security?
No. DeployOpen does not issue audit reports, attestations or certifications. For webhook work, DeployOpen can design and implement signature checking with the secret key, document the flow, prepare logs and evidence of delivery and failure handling, and support readiness work for compliance assessments. Independent auditors and certification bodies perform any formal assessment. Ask your auditor which evidence they will want to see, such as access control for API users and records of configuration changes, and include that in the scope.
What does a fixed-scope quote for this work cover?
A fixed-scope quote follows from a written scope agreed before work starts. It should list the entities and events in scope, each receiving system and its payload requirements, the signature and authentication design, the error handling and retry approach, permissions for API users, testing and cutover steps, documentation and handover, and what is excluded. Each item should name who does what, including what your team must provide. Ask whether the quote includes post-launch support and whether ongoing monitoring or upgrade work is priced separately as a retainer.
What should we ask about security and API user permissions?
Ask how the API user will be scoped and how the secret key will be stored and rotated. The EspoCRM docs describe HMAC as the most secure API authentication method and advise against basic authentication, so confirm the method your design uses. Ask who can read webhook configuration, how receivers verify signatures before processing, and how the skipOwn parameter (available as of version 9.2) will be used to avoid loops. Confirm your EspoCRM version supports every feature in the design before committing to scope.
How DeployOpen scopes and delivers EspoCRM webhook integrations
DeployOpen plans, builds, tests and hands over EspoCRM webhook integrations inside your own infrastructure, starting from a written scope that names each event, receiving system and API user. Engagements are fixed-scope, so the subscriptions, signature checks, retry handling and access rules are agreed before any build work starts.
How we scope
- We list every entity type and event your receiving systems need, including create, update and field-level changes such as assignedUserId.
- We document which fields each receiving system needs, since update events carry only changed attributes.
- We confirm the EspoCRM version and the API user, Webhooks scope and entity permissions the integration will depend on.
How we build
- We create webhook subscriptions through the API, keeping each subscription tied to a dedicated API user with least-privilege roles.
- We implement signature verification with the secret key on the receiving side and test rejection of unsigned or altered payloads.
- We design error handling and retry behavior for failed deliveries, and test how the integration behaves when a receiver is offline.
How we hand over
- You receive subscription definitions, API user roles, signature check code and a runbook describing each event flow.
- We document the retry and failure paths, with the steps your team takes when deliveries stop or a secret must be rotated.
- We walk your engineers through the test cases so they can repeat verification after changes to EspoCRM or the receiving system.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers, so changes to EspoCRM versions can be checked against your subscriptions.
- Upgrade planning covers event behavior changes, such as skipOwn handling introduced in version 9.2, before your production instance is changed.
- Support scope, response expectations and any SLA are written into the retainer agreement, which you should confirm before signing.

