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

# GET /v1/webhooks/{webhook_id}/secret — Get Webhook Signing Key

> GET /v1/webhooks/{webhook_id}/secret returns the full signing key for a webhook subscription, used to verify the X-Knock-Signature header on deliveries.

Every webhook delivery includes an `X-Knock-Signature` header, an HMAC-SHA256 of the raw request body. Call `GET /v1/webhooks/{webhook_id}/secret` to retrieve the full signing key needed to recompute and verify that signature.

## Endpoint

```text theme={null}
GET https://api.knock2.ai/v1/webhooks/{webhook_id}/secret
```

## Required Scope

Your API key must have the `webhooks:read` scope (or the broader `all:read` scope) to call this endpoint.

## Path Parameter

<ParamField path="webhook_id" type="string" required>
  The unique identifier of the webhook subscription. You can find this value in the response from [Create Webhook](/api-reference/webhooks/create-webhook) or [List Webhooks](/api-reference/webhooks/list-webhooks).
</ParamField>

## Parameters

<ParamField query="product_slug" type="string">
  Act on a direct child tenant's webhook subscription instead of your own (multi-tenant partners only). The `X-Knock-Tenant` header takes precedence if both are supplied. See [Reading a Child Tenant's Data](/tenants/managing-tenants#reading-a-child-tenants-data).
</ParamField>

<ParamField header="X-Knock-Tenant" type="string">
  Same as `product_slug` above, as a header instead of a query param. Naming a slug that isn't a direct child of your key returns `404`, never `403`.
</ParamField>

## Example Request

```bash theme={null}
curl https://api.knock2.ai/v1/webhooks/<webhook_id>/secret \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Response

A successful request returns `200 OK` with a `WebhookSecretResponse` body.

```json theme={null}
{
  "data": {
    "signing_key": "3b1e2f9c8a7d6e5f4c3b2a1908f7e6d5c4b3a2918f7e6d5c4b3a29187f6e5d4c"
  }
}
```

<ResponseField name="data.signing_key" type="string">
  The full hex-encoded signing key for this subscription. Use it to verify deliveries: `expected = HMAC-SHA256(signing_key, raw_request_body)`, then compare to the `X-Knock-Signature` header using a constant-time comparison.
</ResponseField>

<Note>
  Unlike an API key, this value is never stored — it's derived deterministically from the subscription ID on every request, so you can fetch it again at any time. There is no "shown only once" restriction, and requesting it repeatedly does not invalidate or rotate it.
</Note>

## Error Responses

| Status | Meaning                                                                                      |
| ------ | -------------------------------------------------------------------------------------------- |
| `401`  | Missing or invalid API key.                                                                  |
| `403`  | Your API key does not have the `webhooks:read` scope.                                        |
| `404`  | No webhook subscription found with the given `webhook_id` for this tenant.                   |
| `422`  | Request could not be processed — verify the `webhook_id` path parameter is correctly formed. |
| `429`  | Rate limit exceeded. Slow down your request rate and retry.                                  |
