> ## Documentation Index
> Fetch the complete documentation index at: https://docs.traceten.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a webhook endpoint

> Register an HTTPS endpoint, capture its signing secret, send a test event, and go live.

## What this lets you do

Go from nothing to a live, verified webhook endpoint receiving signed Traceten events.

## Before you start

* A Traceten account with at least one site tracked.
* An HTTPS endpoint that is publicly reachable. `http://`, `localhost`, and private-network IPs are rejected at registration. For local development, expose your machine with a tunnel such as `cloudflared` or `ngrok` and register the tunnel URL.

## Set up the endpoint

<Steps>
  <Step title="Deploy a handler that returns 2xx fast">
    Your endpoint receives a `POST` with a JSON body. Read the raw body, acknowledge with a 2xx, and queue any heavy work. Traceten times out after 10 seconds and treats a timeout as a failed delivery.

    A minimal handler that accepts everything (you will add signature verification in step 4):

    ```ts theme={null}
    import express from "express";

    const app = express();

    app.post("/traceten-webhooks", express.raw({ type: "application/json" }), (req, res) => {
      const event = JSON.parse(req.body.toString("utf8"));
      console.log(`received ${event.type} (${event.id})`);
      res.status(200).end();
    });

    app.listen(3000);
    ```

    Note `express.raw`, not `express.json`. You need the raw body bytes to verify signatures later.
  </Step>

  <Step title="Register the endpoint in the dashboard">
    Go to [app.traceten.com](https://app.traceten.com), open **Settings → Webhooks**, and click **Add endpoint**. Choose the **Site** the endpoint receives events from (it starts on the site you are viewing), enter the HTTPS URL, and choose the event types to subscribe to. New endpoints default to `ai_session.classified`. Each site can have up to 10 endpoints, and every endpoint in the list shows which site it belongs to.

    On registration, Traceten immediately sends a signed [`webhook.ping`](/webhooks/events#webhookping) to the URL. If your endpoint answers with a 2xx, it is marked **Verified** right away. If it does not (for example, you registered the URL before deploying the handler), the endpoint is created anyway and marked **Unverified**; a failed registration ping never blocks creation. See [verification status](#verification-status) below.
  </Step>

  <Step title="Capture the signing secret">
    On creation, Traceten shows the endpoint's signing secret **once**. Copy it now and store it where your handler can read it, such as an environment variable:

    ```bash theme={null}
    export TRACETEN_WEBHOOK_SECRET="whsec_..."
    ```

    If you lose it, you cannot view it again. Use **Rotate secret** on the endpoint detail page to generate a new one. Rotation is immediate: the old secret stops working the moment the new one is issued, so update your handler's configuration in the same change.
  </Step>

  <Step title="Add signature verification">
    Before trusting any payload, verify the `X-Traceten-Signature` header against the raw body using
    your signing secret. Follow [verifying signatures](/webhooks/verify-signatures) for working Node,
    Python, and Go code, then confirm your implementation against the published conformance vector on
    that page.
  </Step>

  <Step title="Send a test event">
    On the endpoint detail page, click **Send test event**. Traceten delivers a signed [`webhook.ping`](/webhooks/events#webhookping) through the real signing and delivery path and shows the result inline: status code, latency, and a snippet of your response. A 2xx marks the endpoint **Verified**. Test deliveries appear in the [delivery log](/webhooks/delivery#delivery-log) with a **Test** badge.

    Test events work on disabled endpoints too, so you can verify an endpoint before enabling it. The button is rate-limited to 10 sends per minute per endpoint (30 per minute across all your endpoints), and each test gets a single attempt (no retries).

    You are live when the test event returns a 2xx *and* your handler's signature verification passes on it.
  </Step>
</Steps>

## Verification status

Every endpoint carries a verification status, shown as a badge next to the endpoint in **Settings → Webhooks**. It reflects the outcome of the most recent `webhook.ping` (registration or **Send test event**); real event deliveries do not change it.

| Status                   | Meaning                                                                                                              |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| **Verified**             | The most recent ping got a 2xx. The endpoint has proven it can receive and acknowledge a signed delivery.            |
| **Unverified**           | No ping has ever succeeded. Typical for an endpoint registered before its handler was deployed.                      |
| **Verification failing** | The endpoint was verified at some point, but the most recent ping failed. Something that used to work is now broken. |

The transitions are simple: any successful ping moves the endpoint to **Verified** (including from **Verification failing**), and a failed ping moves a **Verified** endpoint to **Verification failing**. An **Unverified** endpoint stays **Unverified** on further failures; it was never proven, so there is nothing to demote.

Verification status is a diagnostic, not a gate. Traceten delivers real events to an enabled endpoint regardless of its verification status.

## Verify it worked

1. Click **Send test event** on the endpoint detail page.
2. The inline result shows a 2xx status.
3. Your handler logs a received `webhook.ping` that passed signature verification.

From this point, real `ai_session.classified` deliveries arrive as your site's AI traffic is classified. If the site is new or low-traffic, that can take a while; the test event is the proof the pipe works.

## Troubleshooting

**Registration rejects the URL.** The URL must be `https://` and publicly resolvable. `http://`, `localhost`, `127.0.0.1`, and private IP ranges are rejected. Use a tunnel for local development.

**Endpoint shows Unverified after registration.** The registration ping did not get a 2xx, usually because the handler was not deployed yet. Deploy it, then click **Send test event**; a 2xx flips the endpoint to Verified.

**Endpoint shows Verification failing.** A previously working endpoint failed its most recent test ping: it returned a non-2xx, took longer than 10 seconds, or was unreachable. Check that the route still accepts `POST` at the exact registered path and responds before doing heavy work, then re-send a test event to re-verify.

**Send test event returns a rate-cap error.** Test sends are limited to 10 per minute per endpoint (30 per minute across all endpoints), and a ping can also be deferred by the endpoint's own delivery rate cap. Wait a moment and click again; a rate-capped ping proves nothing about the endpoint and does not change its verification status.

**Signature verification fails on every delivery.** Almost always a raw-body problem: a framework middleware parsed and re-serialized the JSON before your code hashed it. See [the raw body rule](/webhooks/verify-signatures#verify-the-raw-body-not-a-re-serialized-one).

**Signature verification fails after rotating the secret.** Rotation invalidates the old secret immediately. Update the secret in your handler's environment and redeploy.

**No real events arrive, but test events work.** The pipe is fine; the site has no classified AI sessions yet. Check the [dashboard](https://app.traceten.com) for AI traffic on that site, and confirm the endpoint subscribes to `ai_session.classified` and is enabled.

## Next

* [Verify signatures](/webhooks/verify-signatures)
* [Event reference](/webhooks/events)
* [Delivery semantics](/webhooks/delivery)
