> For the complete documentation index, see [llms.txt](https://docs.insurgrid.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.insurgrid.com/testing/troubleshooting.md).

# Troubleshooting: my webhook isn't arriving

**What this is:** an ordered checklist for the single most common support question — "I registered an endpoint and nothing shows up." Work through it top to bottom; each step is more common than the one after it.

## 1. Is your public tunnel up?

If you're developing locally, InsurGrid can only reach you through a tunnel (ngrok or similar). Tunnels drop when:

* The tunnel process was stopped or your machine went to sleep.
* You're on a free plan whose URL **changes every restart** — and the dashboard still has the old one saved.

**Fix:** confirm the tunnel is running, copy its current `https://…` URL, and make sure it matches what's saved in the API section. See [Expose your local endpoint](/get-started/05-local-development.md).

## 2. Are you returning a 2xx — fast?

The single most common cause once your tunnel or deployment is confirmed up: your endpoint received the request but didn't acknowledge it correctly.

* Confirm your route returns `200` (or any `2xx`) on success — not `204` from a framework default you haven't checked, not a `500` from an unhandled exception in your handler.
* Confirm you're responding **before** doing slow work (database writes, downstream API calls). Do the slow work after you've already replied. See [Set up a webhook endpoint](/get-started/04-webhook-endpoint.md).
* Check your server's own error logs for exceptions thrown during the request — a crash before `res.send(...)` looks identical to "never responded."

## 3. Check the delivery log

Look at the **delivery log** in the API section. It records every attempt InsurGrid made, whether or not your server responded — it tells you where in the chain things actually broke, instead of guessing.

* **Nothing in the log at all** → InsurGrid never tried to deliver. Skip to step 4 (wrong URL/topic/environment) — this isn't a network or code problem yet.
* **An attempt is logged, but it shows a non-`2xx` status or a timeout** → confirms the issue is on your side; revisit step 2.
* **An attempt is logged with `200`** → delivery succeeded on InsurGrid's side. If you still don't see it in your own logs, the problem is downstream of your endpoint (a queue, a crash after the response, a logging gap) — not InsurGrid.

## 4. Is your endpoint URL saved for the right topic and environment?

A URL registered for `client-submission` never receives `form-submission` events, and a **staging** URL never receives events triggered against **production** (or vice versa). Double-check, in the API section:

* The URL is saved under the **topic** you expect (`client-submission` for policy data, `form-submission` for form submissions).
* You're looking at the right **environment** — a test event or a sandbox form submission goes to your **staging** URL, not production.
* The URL is typed correctly, including the path (`/hooks/insurgrid` or whatever route you chose) — a URL that's missing the path but otherwise resolves can silently 404.

## 5. Is your endpoint reachable at all?

* It must be **HTTPS**, not plain HTTP.
* If it sits behind a firewall, load balancer, or VPN, confirm InsurGrid's requests aren't being blocked before they reach your application code.
* Try `curl`-ing your own endpoint's URL from outside your network to confirm it's actually public.

***

## Signature check fails

You're receiving the webhook (it shows in the delivery log and your server logs a request), but your signature verification rejects it.

* **The #1 cause:** you're hashing the parsed-and-re-serialized JSON instead of the **exact raw bytes** InsurGrid sent. Re-serializing a parsed object almost never reproduces the original bytes exactly (whitespace, key order, number formatting can all differ), so the hash won't match even though the request is genuine. Capture the raw body *before* your framework parses it. See [Verify a webhook signature](/guides/signing-verification.md).
* Confirm you're using the **signing secret for the environment the event came from** — a staging secret never validates a production signature, and vice versa.
* Confirm you're comparing against the **`x-signature` header**, not a different header, and that nothing upstream (a proxy, a framework middleware) is modifying the body before your handler sees it.
* If you deliberately configured a different auth scheme (Bearer, Basic, API key) during setup, `x-signature` won't be present at all — check [Register your endpoint & choose an auth type](/guides/register-endpoint-auth-types.md) for what to verify instead.

## Getting duplicates

You're processing the same event more than once.

* This is **expected**, not a bug on InsurGrid's side: delivery is **at-least-once**, so the same event can legitimately arrive twice (for example, if a retry overlaps a slow success on your end).
* The fix lives in your handler, not in delivery: derive a **de-dupe key** and make reprocessing it a no-op — use `data.submission_id` for form submissions, and a **hash of `data`** for policy-data (which has no id field). See [Delivery & de-duplication](/guides/delivery-dedup.md).
* If you're seeing duplicates you can't collapse — form submissions with different `submission_id`s, or policy-data whose contents differ slightly — check whether your endpoint is registered twice (once directly, once through a proxy or a second dashboard entry); that produces two independent deliveries, not one retried delivery.

## Still stuck?

Have your `request_id` or `trace_id` from the delivery log ready and see [Support & contact](/support.md).

## Related

* [Sandbox & testing](/testing/sandbox.md)
* [Integration checklist](/testing/integration-checklist.md)
* [Verify a webhook signature](/guides/signing-verification.md)
* [Delivery & de-duplication](/guides/delivery-dedup.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.insurgrid.com/testing/troubleshooting.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
