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

# Frequently asked questions about Webhook Debugger

> Answers to common questions about webhook URL retention, security, replay, storage, payload handling, and running Webhook Debugger outside Apify.

Find answers to the most common questions about configuring and using Webhook Debugger & Logger. If you need more detail on a specific topic, see the [API reference](/api-reference/overview) or the [input configuration reference](/configuration/input-reference).

<AccordionGroup>
  <Accordion title="How long do webhook URLs stay active?">
    Set `retentionHours` anywhere from 1 to 168 hours (default: 24). Once a webhook expires, it stops accepting traffic immediately. Its logs are purged from the `/logs` query endpoint during the next cleanup cycle, which runs approximately every 10 minutes.

    Reducing `retentionHours` via hot-reload only affects newly generated webhooks — webhooks that already exist keep their original expiry window. Events already written to the Apify Dataset are retained independently of this setting.

    <Tip>
      Call `GET /info` at any time to see each active webhook's `id` and `expiresAt` timestamp.
    </Tip>
  </Accordion>

  <Accordion title="Can I secure my webhook endpoints?">
    Yes. You can layer multiple controls for defense in depth:

    * **`authKey`** — callers must pass the key as `Authorization: Bearer <key>` or `?key=<key>`. When set, webhook ingest also requires this key, so add it to your provider's webhook configuration.
    * **`allowedIps`** — restrict traffic to specific IPs or CIDR blocks.
    * **Signature verification** — automatically verify HMAC signatures from Stripe, GitHub, Shopify, Slack, or a custom provider.
    * **`maskSensitiveData`** — redact `Authorization`, `Cookie`, `Set-Cookie`, and API key headers from stored logs.
    * **`redactBodyPaths`** — redact specific dot-notation paths from the JSON body before storage.

    <Warning>
      Generated webhook URLs are public unless you configure at least one of the above controls. Do not point sensitive production traffic at unsecured endpoints.
    </Warning>
  </Accordion>

  <Accordion title="Does it support replaying captured traffic?">
    Yes. Send a `POST` request to the replay endpoint with a destination URL:

    ```bash theme={null}
    curl -X POST \
      "https://<run-id>.runs.apify.net/replay/<webhookId>/<itemId>?url=https://target.example/webhook"
    ```

    The actor strips masked and transport-managed headers before forwarding, then adds `X-Apify-Replay: true`, `X-Original-Webhook-Id`, and an `Idempotency-Key` to the outbound request. The response includes the target's status code and body, plus a `strippedHeaders` list when any headers were removed.

    <Note>
      The `url` query parameter is required. Replay targets that resolve to private or reserved IP ranges are blocked by SSRF protection.
    </Note>
  </Accordion>

  <Accordion title="Can I use it as a mock callback server?">
    Yes. Configure the response behavior using these input fields:

    | Input                    | Purpose                                               |
    | ------------------------ | ----------------------------------------------------- |
    | `defaultResponseCode`    | HTTP status code returned to the sender               |
    | `defaultResponseBody`    | Response body (JSON, XML, or plain text)              |
    | `defaultResponseHeaders` | Extra headers included in every response              |
    | `responseDelayMs`        | Artificial delay added after processing (0–10,000 ms) |

    You can also override the status code on a per-request basis by appending `?__status=<code>` to the webhook URL. This is useful for simulating 4xx or 5xx responses without changing the global configuration.
  </Accordion>

  <Accordion title="Where are captured events stored?">
    Captured events are stored in three places with different roles:

    * **Apify Dataset** — the durable source of truth. Every captured event is written here and retained independently of `retentionHours`.
    * **DuckDB** — a local read model that mirrors Dataset records for fast filtering and pagination at `/logs`. DuckDB syncs asynchronously, so there may be a short lag before new events appear in query results.
    * **Apify Key-Value Store** — holds active webhook state and stores large payload offloads that exceed the in-memory threshold.

    <Tip>
      If `/logs` doesn't show a recent event yet, check `/system/metrics` to see the current Dataset-to-DuckDB sync status.
    </Tip>
  </Accordion>

  <Accordion title="What happens with large payloads?">
    Requests whose body exceeds `maxPayloadSize` (default: 10 MB) are rejected with a `413` status.

    For payloads that are within the size limit but above the Key-Value Store offload threshold, the actor stores the payload content in KVS rather than keeping it in the in-memory read model. The event remains fully queryable via `/logs`, and you can retrieve the full payload on demand by calling `GET /logs/:logId/payload`.
  </Accordion>

  <Accordion title="Can I run it outside Apify?">
    Yes. You can run Webhook Debugger & Logger locally or in your own infrastructure without an Apify account.

    **Node.js (20+):**

    ```bash theme={null}
    npm install
    npm start
    ```

    **Docker:**

    ```bash theme={null}
    docker build -t webhook-debugger-logger .

    docker run --rm -p 8080:8080 \
      -e ACTOR_WEB_SERVER_PORT=8080 \
      -e APIFY_LOCAL_STORAGE_DIR=/app/storage \
      webhook-debugger-logger
    ```

    The server listens on port 8080 by default. Use the `ACTOR_WEB_SERVER_PORT` environment variable to change the port, and pass your actor input as the `INPUT` environment variable.
  </Accordion>

  <Accordion title="How many webhook URLs can I create?">
    You can create between 1 and 50 webhook URLs per run using the `urlCount` input (default: 3). Each URL gets its own unique ID and expiry time.

    All endpoints generated in a single run share the same configuration settings for auth, signature verification, response mocking, and forwarding.
  </Accordion>

  <Accordion title="Does it support JSON Schema validation?">
    Yes. Set the `jsonSchema` input to a valid JSON Schema string. Incoming payloads that do not match the schema are rejected with a `400 Bad Request` response before the event is stored.

    ```json theme={null}
    {
      "jsonSchema": "{\"type\":\"object\",\"required\":[\"event\"],\"properties\":{\"event\":{\"type\":\"string\"}}}"
    }
    ```

    This is useful for enforcing a contract during integration testing without adding validation logic to your own service.
  </Accordion>

  <Accordion title="Can I transform payloads before storage?">
    Yes. The `customScript` input accepts a JavaScript snippet that runs before the event is stored. Use it to normalize shapes, inject debug metadata, strip or remap fields, or mark events for downstream routing.

    ```javascript theme={null}
    if (event.contentType === 'application/json') {
      const body = typeof event.body === 'string' ? JSON.parse(event.body) : event.body;
      event.body = { ...body, normalized: true, source: 'webhook-debugger' };
    }
    ```

    Scripts run in a sandboxed worker isolate. They have access to `event`, a read-only copy of `req`, `console`, and `HTTP_STATUS`, but no access to the filesystem, network, `process`, or `require`. `eval()` and `Function()` are disabled. If a script fails or times out, the error is logged and the capture pipeline continues normally.
  </Accordion>

  <Accordion title="How does hot-reload work?">
    Hot-reload lets you apply input changes without restarting the actor.

    * **On Apify:** the runtime polls the Key-Value Store for an updated actor input.
    * **Locally:** it watches `storage/key_value_stores/default/INPUT.json` for changes.

    Reloadable settings include rate limiters, auth, retention, replay settings, forwarding settings, and parser limits. Changes take effect for new requests immediately after the reload is applied.

    Set the environment variable `DISABLE_HOT_RELOAD=true` if you want a fixed configuration for reproducible test runs.

    <Note>
      Reducing `retentionHours` via hot-reload only affects new webhooks. Existing webhooks keep their original expiry window.
    </Note>
  </Accordion>

  <Accordion title="What events can trigger alerts?">
    You can configure alerts to fire on any combination of these conditions:

    | Trigger             | When it fires                                |
    | ------------------- | -------------------------------------------- |
    | `error`             | An internal processing error occurs          |
    | `4xx`               | The captured request results in a 4xx status |
    | `5xx`               | The captured request results in a 5xx status |
    | `timeout`           | A forwarding or replay attempt times out     |
    | `signature_invalid` | Signature verification fails                 |

    Configure a Slack or Discord webhook URL in the `alerts` input and list the triggers you want in `alertOn`:

    ```json theme={null}
    {
      "alerts": {
        "slack": { "webhookUrl": "https://hooks.slack.com/services/..." }
      },
      "alertOn": ["error", "4xx", "5xx", "timeout", "signature_invalid"]
    }
    ```
  </Accordion>
</AccordionGroup>
