Perfex CRM Webhook Not Firing: A Debugging Checklist
Perfex CRM webhook not firing? Six things break, and only one is the destination. Work the checks from cheapest to most invasive and find it in ten minutes.
The Zap has not run in four days. The Slack channel that used to announce new leads has gone quiet. Nobody changed anything, or at least nobody changed anything they remember changing. The webhook is still in the list, still enabled, still pointing at a URL that returns a cheerful 200 when you paste it into a browser.
A Perfex CRM webhook not firing gives you almost nothing to work with. No red banner, no error email, no failed job on a dashboard. The CRM’s position is that it did what it was asked; the receiving service’s position is that it never heard from anyone. Both are usually telling the truth, and the fault sits at one of six identifiable points between them.
We build and sell the Webhooks module for Perfex CRM and the REST API module whose Webhooks 2.0 layer is referenced below, so weigh the recommendations accordingly. The diagnostic order is the one we use on support tickets, and most of it transfers to any outbound webhook system.
A Perfex CRM webhook not firing is six failures wearing one symptom
Before touching a setting, decide which of the six you have. The delivery log answers that in about thirty seconds, and every step after it depends on the answer.
| What the log shows | Where it broke | What to check |
|---|---|---|
| No entry at all | The event never fired | Event coverage, how the record was created |
| Entry stuck queued or pending | The delivery never left the queue | Cron, delivery mode |
| Connection error or timeout | The request was blocked or the host is unreachable | SSRF strict mode, TLS, DNS, firewall |
401 or 403 | The destination rejected the payload | Signature verification, auth headers |
404 or 405 | Wrong URL or wrong HTTP method | Path, GET versus POST |
2xx | It fired and was accepted | The problem is downstream, not in Perfex |
The common mistake is skipping this and re-creating the webhook. A rebuilt webhook that also does not fire teaches you nothing and costs you the original configuration.
Start with the two reads that cost nothing
The Perfex CRM webhook delivery log
Both modules keep a filterable delivery log, which matters once you have a few dozen webhooks and thousands of rows. Log filtering and the test-webhook feature both arrived in version 2.0.0 of the Webhooks module, so if your install predates that, upgrading is a legitimate first move rather than a deflection.
Filter to the webhook you care about and read the most recent attempt against the table above. An entry predating the outage means Perfex stopped trying. Fresh entries carrying non-2xx responses mean it is trying and being refused.
When the Perfex webhook test failed
The test-webhook button fires a sample request at the configured URL so you can confirm the destination responds before you rely on it. It splits the problem in half faster than anything else, but read the result precisely, because it proves less than people assume.
A failed test is a transport or authentication problem, and you can now work on it without waiting for a real lead to arrive. A successful test with no real deliveries is the trap: it tells you the URL is reachable and your headers are accepted, and nothing whatsoever about whether your event fires. That green tick sends people off hunting for a bug in Zapier when the CRM never generated the job.
No log entry at all: the event was never hooked
An empty log is not a delivery failure. It means the job was never created. Three realistic reasons.
The event does not exist for that object. Coverage is not uniform. In the Webhooks module, Update is not supported on Tasks, and Lead deletion uses the core action that runs before deletion rather than after it. Proposals carry their own Accept, Decline and Sent events. Version 2.0.0 widened the catalogue considerably - credit notes, items and products, task status changes and comments, estimate accepted or converted to invoice, lead marked lost or junk, invoice status changed and sent - so a hook that “should exist” may simply not exist on your version. Check the version before anything else.
The record was not created through Perfex’s own code path. This is the one that eats afternoons. Webhooks hang off PHP action hooks. A row that appears in tblleads because an import script ran a raw INSERT, or because another module wrote to the table directly, fires nothing at all, because no hook was ever reached. The record is genuinely there. The event genuinely never happened.
The test takes thirty seconds: create the same kind of record by hand in the admin UI. If the manual one fires and the automated one does not, stop debugging the webhook. The problem is the thing doing the writing.
The change arrived over the API and your bridge only covers the admin side. This is the gap Webhooks 2.0 closes: its catalogue of 124 events across 22 resource groups is bridged to 75 core Perfex actions, so admin-side and API-side changes both fire. If your integrations create records over the API while your automations only ever see records made by staff clicking buttons, that asymmetry is your answer.
Logged but never delivered: the queue, the cron and webhook retries
If the log shows the job created but never completed, this is a scheduling problem, not a networking one.
Delayed triggers need their own cron. The Webhooks module can hold a webhook and send it minutes, hours or days after the event, which is ideal for follow-ups. That feature requires a custom cron job. Configure a two-hour delay on an install with no cron and the job queues correctly, permanently.
Async delivery is queue-driven too. Webhooks 2.0 can deliver asynchronously: a background queue claims each job atomically and retries failures with exponential backoff, driven by cron with an admin-load fallback for installs that have none. That fallback produces one of the strangest reports in this category - webhooks only fire when I am logged into the admin panel. Nothing is broken. Nothing is draining the queue except your page loads.
Do not manually re-fire something mid-backoff. Exponential backoff means an entry that looks stuck may be legitimately waiting its turn. Pushing it into a destination that is not idempotent creates the duplicate you spend tomorrow deleting. Read the attempt count first.
To isolate the queue, switch delivery mode to synchronous in the platform settings panel and trigger a real event. If it arrives instantly, the queue is your problem - and the fix is a working cron, not synchronous delivery forever, which puts the destination’s latency into your staff’s page load.
Delivered but rejected: signatures, headers and methods
A 401, 403 or 405 is good news. The request left, arrived, and was understood well enough to be refused.
- Signature verification. Webhooks 2.0 signs every request with an HMAC and a timestamp in
X-Perfex-Signature, giving verification and replay protection. If your receiver computes the signature over a re-serialised copy of the JSON rather than the raw request body it will never match - one whitespace difference is enough. And because the timestamp guards against replay, a receiver whose clock has drifted rejects valid deliveries as stale. Check NTP before you touch the secret. - Missing auth headers. The Webhooks module ships 32 standard request headers and accepts any custom header you add, which is how you authenticate against a third-party API. A Zapier Catch Hook does not care. A real API endpoint will
401you all day over a missingAuthorizationheader. - Method mismatch.
GET,POST,PUT,PATCHandDELETEare all available, plus a dedicated Status Change trigger for Leads. A webhook configured asGETagainst an endpoint that only acceptsPOSTreturns405, every time, forever. - Token permissions. The REST API module’s permission editor covers webhooks like any other capability, so if you manage them over the API, a management call that
403s is a token grant problem rather than a webhook problem.
Blocked before the request ever left: SSRF strict mode and TLS
The last category catches people running their automation stack on the same box as the CRM.
Webhooks 2.0 guards deliveries with SSRF filtering and TLS verification, and an optional strict mode blocks private-LAN targets outright. That is a deliberate control: it stops a careless or compromised webhook probing your internal network from inside the perimeter. It also means a webhook pointed at http://192.168.1.50:5678/webhook - a self-hosted n8n instance one hop away - is refused by design, with a connection-level failure indistinguishable from a firewall drop.
Two honest options: give the receiver a public hostname and a real certificate, or turn strict mode off knowing exactly what you gave up. Do not disable TLS verification to work around a self-signed certificate on an internal receiver - fix the certificate, because that toggle applies to every destination you have, not just the awkward one.
The run sheet
Work it in this order and stop at the first check that explains the symptom:
- Open the delivery log, filter to the webhook, read the most recent attempt and its response code.
- Fire the test webhook, remembering that success proves only reachability and auth.
- No log entries? Create the same record manually in the admin UI. If that fires, the writer bypassed the action hooks.
- Still no entries? Confirm the event exists for that object on your module version, and that
Updateis not being expected on Tasks. - Entries queued but never sent? Check the cron is running, and that delayed triggers have the custom cron they require.
401or403? Verify the HMAC over the raw body, check the receiver’s clock, confirm the auth header is present.405or404? Compare the configured method and path against what the endpoint actually accepts.- Connection error to a private address? SSRF strict mode is doing its job. Move the receiver to a public hostname.
If all eight come back clean and the log shows 2xx, Perfex has done everything it was asked. Take that log entry, with its timestamp and response body, to whoever owns the receiving service - that record is the whole argument.