Skip to content

PandaDoc Webhook Not Firing? Complete Diagnostic Guide

Pure Proposals
PandaDoc Webhook Not Firing? Complete Diagnostic Guide

A PandaDoc webhook that does not fire looks identical to one that fires perfectly into a black hole. There is no error surfaced to the sender, no toast in the PandaDoc UI, and no clue that anything is wrong until a downstream automation stops running. This guide is a diagnostic runbook for engineers debugging a silent webhook, ordered from most to least common cause.

Quick fix checklist

  • Confirm the trigger name you subscribed to is a real, current PandaDoc trigger. document_completed is not a valid trigger name and is the single most common silent failure.
  • Check PandaDoc’s webhook delivery logs in the subscription UI for delivery attempts, response codes, and last-failure timestamp.
  • Hit your endpoint from outside your network with a POST and confirm it returns 2xx within a few seconds.
  • Verify TLS: your endpoint must be reachable over HTTPS with a valid, non-self-signed certificate.
  • Confirm the subscription is scoped to the correct workspace and, if applicable, the correct user’s documents.
  • If you use signature verification, log the raw request body and the computed signature before comparing, so you can see mismatches instead of silently rejecting.

How does a PandaDoc webhook actually work end to end?

A PandaDoc webhook has three moving parts: a subscription record (the config saying “when X happens, POST to Y”), a trigger event (something PandaDoc detects in your workspace), and a delivery attempt (the actual outbound HTTP call). A failure at any of the three looks identical from the outside: nothing arrives.

The subscription lives inside your PandaDoc workspace. It has a target URL, a list of triggers it cares about, an optional shared secret for signing, and metadata about which workspace and user context it belongs to. When an event happens in the workspace, PandaDoc’s internal event bus matches it against every active subscription, builds a payload, and enqueues a delivery. The delivery worker then POSTs that payload to your URL and records the response.

Any silent failure lives in one of those three layers. The diagnostic order below reflects which layer is most often responsible in production.

Why is the wrong trigger the number-one cause of silent failure?

Because trigger names are strings, and PandaDoc happily accepts a subscription for a trigger that will never fire. The classic trap: engineers guess document_completed based on the UI language. That trigger does not exist. The document lifecycle changes are surfaced through document_state_changed (with a state field in the payload) or via role-specific triggers such as recipient_completed.

When you subscribe to a name that PandaDoc does not recognise as an active trigger, the subscription is created without complaint. It just never receives an event. If your webhook has been up for hours and nothing has arrived after several state transitions, this is almost certainly the cause. Check the trigger list in PandaDoc’s webhook subscription documentation and compare exact strings.

Common valid triggers and payload shape

TriggerFires whenKey payload signal
document_state_changedAny change to document status (draft, sent, viewed, completed, declined, etc.)data.status reflects the new state
document_updatedContent, recipients, tokens, or pricing changed on a documentdata.id plus updated fields
document_creation_startedA document begins generation from a templatedata.id, data.template.id
recipient_completedA specific recipient finishes their required actionsdata.recipient info
document_section_addedA section is added to a document (relevant for content-library flows)Section identifiers

Refer to PandaDoc’s official webhook subscription documentation for the current, authoritative list. Trigger names change over time, and PandaDoc has deprecated older names in past releases.

Why does the endpoint URL fail even though it looks right?

Because “reachable from my laptop” and “reachable from PandaDoc’s delivery workers” are two different tests. The URL saved in the subscription can be typo-free and still unreachable: DNS not propagated, firewall blocking the outbound source ranges, a load balancer routing rule that only accepts requests from a specific VPC, or a staging URL left in place after a promotion to prod.

A five-minute check that catches most of this: run curl -X POST -d '{}' -H 'Content-Type: application/json' https://your-endpoint/pandadoc-webhook from a machine outside your infrastructure (a home connection, a cloud shell, or a serverless function). If that curl does not return 2xx promptly, PandaDoc will not either. Also verify the URL in the subscription character-for-character, including any trailing slash. Some frameworks 301 from /webhook to /webhook/ and drop the POST body on the redirect, producing a silent-looking failure with a real 301 code.

What happens when your endpoint returns non-2xx to a delivery?

PandaDoc treats any non-2xx response as a failed delivery and enters a retry-and-backoff cycle. After enough consecutive failures, the subscription is typically deactivated or throttled to protect against dead endpoints, at which point later legitimate events also stop arriving until the subscription is re-enabled.

Common triggers for non-2xx that developers miss:

  1. Auth middleware rejecting the delivery. Your framework’s session middleware sees no cookie and returns 401 before the webhook handler runs.
  2. CSRF protection rejecting POSTs without a token. Same failure shape.
  3. Body parsers that require a specific Content-Type and reject others as 415.
  4. Application-level validators that reject unexpected payload fields (extra fields PandaDoc adds in a new release).
  5. Handler throwing an uncaught exception and the framework returning 500.

Inspect your endpoint’s own access logs for the timestamp of any delivery attempt and confirm the response code. If the request never arrived at your access log, the failure is upstream (URL, DNS, firewall, TLS). If it arrived and returned non-2xx, the failure is in your handler stack.

Are TLS and HTTPS issues really that common?

Yes, and they are easy to miss because a browser will accept certificates that a webhook client will refuse. PandaDoc’s delivery workers validate the certificate chain strictly: self-signed certs, expired certs, wrong CN or SAN, and incomplete intermediate chains all cause the delivery to fail before the request body ever leaves the sender.

Test with curl (no -k) or a service like SSL Labs. If the tool warns about a chain issue, PandaDoc will silently drop deliveries there. Two very common versions of this: a Let’s Encrypt cert that renewed but the new cert never got installed on the load balancer, and a wildcard cert that covers *.example.com but not webhooks.example.com because of the level-of-subdomain rule.

How do you debug a signature mismatch on incoming payloads?

Log the raw request body bytes and the header carrying the signature, compute your own HMAC using the shared secret, and log both values before comparing. Never reject silently: a 401 for a bad signature should log which side of the comparison you rejected, ideally with the first and last few bytes of the body.

Signature verification bugs share a small set of causes:

  • Body reparsed before hashing. If your framework parses JSON then re-serialises it, the byte sequence you hash no longer matches what PandaDoc hashed. Hash the raw body from the request stream before any parsing.
  • Wrong secret. The secret in the subscription and the secret in the endpoint’s env drifted. Rotate deliberately and update both sides in the same deploy.
  • Wrong algorithm or encoding. HMAC-SHA256 vs SHA1, hex vs base64, uppercase vs lowercase. Read PandaDoc’s docs for the exact scheme.
  • Whitespace or newline injection. A trailing newline on the secret in a .env file will produce a completely different signature.

How do PandaDoc’s webhook delivery retries actually work?

PandaDoc retries failed deliveries on a backoff schedule, up to a bounded number of attempts. After the final attempt, the delivery is marked permanently failed and the event is not resent. The subscription itself may be flagged as unhealthy after enough consecutive failures.

Two implications for your handler design. First, your endpoint must be idempotent: the same event may arrive twice if a delivery times out but the handler already processed it. Store an event or delivery ID and short-circuit on repeat. Second, do not block the delivery on downstream work. Acknowledge with 200 as soon as the payload is durable in your own queue, then process asynchronously. A ten-second downstream call that times out will look to PandaDoc like a failed delivery and burn a retry.

How do you inspect PandaDoc’s webhook delivery logs?

Open the subscription in the PandaDoc UI. Each subscription surfaces a delivery history: the events that matched it, the timestamp of each delivery attempt, the response code returned by your endpoint, and often the response body snippet for failed attempts. This is the fastest way to distinguish “PandaDoc never tried” from “PandaDoc tried and your endpoint said no.”

If the log shows zero delivery attempts for events you know happened, the failure is at the subscription or trigger layer: wrong trigger name, wrong workspace scope, or the subscription is disabled. If the log shows attempts with non-2xx responses, the failure is at your endpoint. If it shows attempts with connection errors (timeouts, TLS failures, DNS errors), the failure is at your network edge.

Why do webhooks fire in one workspace but not another?

Because PandaDoc subscriptions can be scoped to a specific workspace or user context, and events raised outside that scope will not match. If your engineering team owns a shared automation workspace but sales reps live in different workspaces, a subscription created in the automation workspace will never see events from documents created under a rep’s workspace.

This is a frequent trap when a company grows into multi-workspace usage. Audit which workspace the subscription belongs to, which workspace the document that should have triggered it was created in, and whether the event source (a template, an integration, an API key) is scoped to a workspace that the subscription can see.

The same class of problem applies to CRM-created documents. If HubSpot creates a document through the native integration and the subscription is scoped to a workspace where that integration is not installed, the trigger fires in a workspace your subscription cannot observe. Reviewing the PandaDoc HubSpot integration mapping alongside your webhook scope is often the fastest way to unpick this.

What is the right diagnostic order when a webhook stops working?

Work from the cheapest and most common cause to the rarest and hardest, in this order:

  1. Open the subscription in the PandaDoc UI and check the delivery log for the last 24 hours. Zero attempts vs failed attempts splits the problem in half.
  2. Confirm the trigger names are on the current valid list. Reject document_completed and anything else you cannot find in the docs.
  3. Confirm the URL character-by-character, including scheme, host, path, and trailing slash. Test with curl from outside your infrastructure.
  4. Confirm TLS chain validity with a strict client (no -k).
  5. Check your endpoint’s own access logs for the delivery timestamps. Presence tells you where the failure sits.
  6. If deliveries land but return non-2xx, walk the middleware stack: auth, CSRF, body parsers, handler exceptions.
  7. If signatures are enabled, log both sides of the comparison before rejecting.
  8. Confirm workspace scope: the subscription workspace and the document workspace must line up.
  9. Confirm the subscription is still marked active. Sustained failures can auto-disable it.

For teams running many webhook flows across multiple products or workspaces, this ordering is worth turning into a runbook that on-call engineers work through instead of debugging from scratch. Orchestration platforms like the Proposal Engine can also absorb some of this fragility by centralising delivery, retries, and observability rather than leaving every product to reinvent them.

FAQ

Is document_completed a valid PandaDoc webhook trigger? No. It is one of the most common silent-failure traps. Use document_state_changed and filter on the state (or equivalent status) field in the payload, or use a recipient-scoped trigger such as recipient_completed where appropriate.

Why does my webhook fire in staging but not production? Nearly always workspace scope or URL. Staging and production subscriptions usually live in different PandaDoc workspaces, and the document that triggered in staging may have been created by a workspace user that production cannot see. Also confirm the production URL is public, not a private hostname only reachable from a VPN.

Do PandaDoc webhooks retry forever? No. PandaDoc retries on a bounded backoff schedule and then gives up on that event. Repeated failures can also deactivate the subscription. Design handlers to acknowledge fast and process asynchronously so a downstream slowness does not burn retries.

Why does signature verification fail on payloads that clearly came from PandaDoc? Because the body was reparsed before hashing, the wrong algorithm or encoding was used, or the shared secret has stray whitespace. Always hash the raw request body bytes, log the two signatures on mismatch, and confirm the secret matches exactly on both sides.

Where do I find the actual delivery history? Inside the PandaDoc UI on the subscription itself. It shows matched events, timestamps, response codes, and error messages. This is the fastest way to prove whether PandaDoc even attempted delivery.

Get help debugging your PandaDoc webhooks

If you have worked through the checklist and the deliveries still are not landing, get engineering-grade help. Pure Proposals runs PandaDoc troubleshooting and integration help for teams whose webhook, template, or integration setup is silently costing them deals. Send us your subscription details and endpoint logs and we will get the deliveries flowing.