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

# Test webhook

> Send a test payload to verify a webhook endpoint is reachable and responding

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="circle-check" color="#16a34a">
  **Free** - does not consume credits
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/webhooks/15/test \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    | jq --arg webhook_id "15" '{
      webhook_id: $webhook_id,
      accepted: (.success == true),
      status_code: .statusCode,
      error: (.error // null)
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/webhooks/15/test", {
    method: "POST",
    headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
  });
  const result = await response.json();
  const testOutcome = {
    webhook_id: "15",
    accepted: result.success === true,
    status_code: result.statusCode,
    error: result.error ?? null,
  };
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://xquik.com/api/v1/webhooks/15/test",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
  )
  result = response.json()
  test_outcome = {
      "webhook_id": "15",
      "accepted": result["success"] is True,
      "status_code": result["statusCode"],
      "error": result.get("error"),
  }
  ```

  ```go Go theme={null}
  package main

  import (
    "encoding/json"
    "log"
    "net/http"
  )

  type TestOutcome struct {
    WebhookID  string  `json:"webhook_id"`
    Accepted   bool    `json:"accepted"`
    StatusCode int     `json:"status_code"`
    Error      *string `json:"error"`
  }

  func main() {
  	req, err := http.NewRequest("POST", "https://xquik.com/api/v1/webhooks/15/test", nil)
  	if err != nil {
  		log.Fatal(err)
  	}
  	req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		log.Fatal(err)
  	}
  	defer resp.Body.Close()

    var result struct {
      Error      *string `json:"error"`
      StatusCode int     `json:"statusCode"`
      Success    bool    `json:"success"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
  		log.Fatal(err)
  	}
    outcome := TestOutcome{
      WebhookID:  "15",
      Accepted:   result.Success,
      StatusCode: result.StatusCode,
      Error:      result.Error,
    }
    _ = outcome
  }
  ```
</CodeGroup>

These snippets shape a small deployment-check record. Store `accepted`,
`status_code`, and `error` with the webhook ID instead of printing the full
test response.

## Path parameters

<ParamField path="id" type="string" required>
  The webhook ID to test.
</ParamField>

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. This endpoint also accepts session cookie authentication.
</ParamField>

## What happens

Xquik sends a `webhook.test` event to your endpoint, HMAC-signed with the webhook's secret:

```json Payload delivered to your endpoint theme={null}
{
  "eventType": "webhook.test",
  "data": {
    "message": "Test delivery from Xquik"
  },
  "timestamp": "2026-02-27T12:00:00.000Z"
}
```

The signed request includes the `X-Xquik-Signature`, `X-Xquik-Timestamp`, and
`X-Xquik-Nonce` headers. Verify the signature against the raw request body
before parsing JSON, reject stale timestamps, and de-dupe recent nonces exactly
as you do for production monitor deliveries.

You can test active, paused, or needs-attention webhooks. This endpoint reports
whether the receiver accepted the signed test request, but it does not change
`isActive`, `deliveryStatus`, or `consecutiveFailures`. Use
[Resume Webhook](/api-reference/webhooks/resume) when a fixed receiver should
pass a signed test before delivery resumes.

The test endpoint does not return or rotate the signing secret. Keep using the
secret returned by [Create Webhook](/api-reference/webhooks/create) for
signature verification, and keep raw request bodies, raw signatures, and full
headers out of deployment logs.

`webhook.test` payloads include `eventType`, `data`, and `timestamp`. They do
not include `deliveryId` or `streamEventId`, so use them for reachability and
signature checks rather than receiver idempotency checks.

## Response

<Tabs>
  <Tab title="200 OK (success)">
    <ResponseField name="success" type="boolean">
      `true` when your endpoint responded with a 2xx status code.
    </ResponseField>

    <ResponseField name="statusCode" type="number">
      The HTTP status code returned by your endpoint.
    </ResponseField>

    ```json theme={null}
    {
      "success": true,
      "statusCode": 200
    }
    ```
  </Tab>

  <Tab title="200 OK (delivery failed)">
    <ResponseField name="success" type="boolean">
      `false` when your endpoint returned a non-2xx status or was unreachable.
    </ResponseField>

    <ResponseField name="statusCode" type="number">
      The HTTP status code returned by your endpoint, or `0` if unreachable.
    </ResponseField>

    <ResponseField name="error" type="string">
      Error description (e.g. `"HTTP 500"` or a network error message).
    </ResponseField>

    ```json theme={null}
    {
      "success": false,
      "statusCode": 500,
      "error": "HTTP 500"
    }
    ```
  </Tab>

  <Tab title="400 Invalid Request">
    ```json theme={null}
    { "error": "invalid_id", "message": "Invalid webhook ID format" }
    ```

    The provided webhook ID is not a valid format.
  </Tab>

  <Tab title="401 Unauthenticated">
    ```json theme={null}
    { "error": "unauthenticated", "message": "Missing or invalid API key" }
    ```

    Missing or invalid API key / session cookie.
  </Tab>

  <Tab title="404 Not Found">
    ```json theme={null}
    { "error": "not_found", "message": "Webhook not found" }
    ```

    No webhook exists with this ID, or it belongs to a different account.
  </Tab>

  <Tab title="429 Rate Limited">
    ```json theme={null}
    { "error": "rate_limit_exceeded", "message": "Too many requests. Try again later.", "retryAfter": 60 }
    ```

    Too many requests. Wait for the `Retry-After` header before retrying.
  </Tab>
</Tabs>

## Test result handoff

Use this endpoint before routing production monitor events to a new receiver or
after changing webhook code, secrets, firewall rules, or queue routing.

<CardGroup cols={2}>
  <Card title="Receiver accepted" icon="circle-check">
    Treat `success: true` and a `2xx` `statusCode` as proof that the receiver
    accepted the signed `webhook.test` request.
  </Card>

  <Card title="Receiver rejected" icon="triangle-alert">
    Treat `success: false` with a non-`2xx` `statusCode` as a receiver error.
    Fix the endpoint before waiting for the next production monitor event.
  </Card>

  <Card title="Endpoint unreachable" icon="wifi-off">
    Treat `statusCode: 0` as a network or reachability failure. Check DNS,
    TLS, firewall rules, and the public HTTPS URL.
  </Card>

  <Card title="Error string" icon="file-warning">
    Store `error` with your deployment logs so support, queue, or incident
    workers can see the latest test failure reason.
  </Card>

  <Card title="Paused or Needs Attention" icon="toggle-left">
    Tests are still sent to paused and needs-attention webhooks. A passing test
    proves reachability only; use [Resume Webhook](/api-reference/webhooks/resume)
    when delivery should resume after the test.
  </Card>

  <Card title="Signature path" icon="shield-check">
    Validate `X-Xquik-Signature`, `X-Xquik-Timestamp`, and
    `X-Xquik-Nonce` on the raw request body before accepting test or production
    events.
  </Card>

  <Card title="Test payload" icon="file-json">
    `webhook.test` payloads include `eventType`, `data`, and `timestamp`.
    They do not include `deliveryId` or `streamEventId`.
  </Card>

  <Card title="Production triage" icon="activity">
    After the receiver accepts this signed test, use
    [List Deliveries](/api-reference/webhooks/deliveries) to debug real monitor
    events. Delivery rows contain `id`, `streamEventId`, `status`, `attempts`,
    `lastStatusCode`, `lastError`, `createdAt`, and `deliveredAt`.
  </Card>

  <Card title="Event join" icon="link">
    For failed or exhausted production deliveries, use delivery `streamEventId`
    as the `{id}` for [Get Event](/api-reference/events/get). Store the event
    `monitorId`, `monitorType`, `type`, `occurredAt`, and `data` with the
    receiver incident.
  </Card>
</CardGroup>

<Note>
  This endpoint supports **dual authentication**: API key (`x-api-key` header) or session cookie from the dashboard.

  **Related:** [List Webhooks](/api-reference/webhooks/list) · [Resume Webhook](/api-reference/webhooks/resume) · [List Deliveries](/api-reference/webhooks/deliveries) · [Get Event](/api-reference/events/get) · [Webhook Verification](/webhooks/verification)
</Note>
