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

# Bring Your Own Guardrails

> Integrate your custom guardrails with Portkey using webhooks

Portkey's webhook guardrails allow you to integrate your existing guardrail infrastructure with our AI Gateway. This is perfect for teams that have already built custom guardrail pipelines (like PII redaction, sensitive content filtering, or data validation) and want to:

* Enforce guardrails directly within the AI request flow
* Make existing guardrail systems production-ready
* Modify AI requests and responses in real-time

## How It Works

1. You add a Webhook as a Guardrail Check in Portkey
2. When a request passes through Portkey's Gateway:
   * Portkey sends relevant data to your webhook endpoint
   * Your webhook evaluates the request/response and returns a verdict
   * Based on your webhook's response, Portkey either allows the request to proceed, modifies it if required, or applies your configured guardrail actions

## Setting Up a Webhook Guardrail

### Configure Your Webhook in Portkey App

<Frame>
  <img src="https://mintcdn.com/portkey-docs/QKXLB-54q6gEhIad/images/guardrails/webhook.png?fit=max&auto=format&n=QKXLB-54q6gEhIad&q=85&s=2d84886ae193f885e78f9edbffbd87be" width="350" data-path="images/guardrails/webhook.png" />
</Frame>

In the Guardrail configuration UI, you'll need to provide:

| Field           | Description                              | Type          |
| :-------------- | :--------------------------------------- | :------------ |
| **Webhook URL** | Your webhook's endpoint URL              | `string`      |
| **Headers**     | Headers to include with webhook requests | `JSON`        |
| **Timeout**     | Maximum wait time for webhook response   | `number` (ms) |

#### Webhook URL

This should be a publicly accessible URL where your webhook is hosted.

<Note>
  **Enterprise Feature**: Portkey Enterprise customers can configure secure access to webhooks within private networks.
</Note>

#### Headers

Specify headers as a JSON object:

```json theme={"system"}
{
  "Authorization": "Bearer YOUR_API_KEY",
  "Content-Type": "application/json"
}
```

#### Timeout

The maximum time Portkey will wait for your webhook to respond before proceeding with a default `verdict: true`.

* Default: `3000ms` (3 seconds)
* If your webhook processing is time-intensive, consider increasing this value

### Webhook Request Structure

Your webhook should accept `POST` requests with the following structure:

#### Request Headers

| Header         | Description                                  |
| :------------- | :------------------------------------------- |
| `Content-Type` | Always set to `application/json`             |
| Custom Headers | Any headers you configured in the Portkey UI |

#### Request Body

Portkey sends comprehensive information about the AI request to your webhook:

<Expandable title="Webhook Request Structure">
  <ParamField body="request" type="object">
    Information about the user's request to the LLM

    <Expandable title="Properties">
      <ParamField body="json" type="object">OpenAI compliant request body json.</ParamField>
      <ParamField body="text" type="string">Last message/prompt content from the overall request body.</ParamField>
      <ParamField body="isStreamingRequest" type="boolean">Whether the request uses streaming</ParamField>
    </Expandable>
  </ParamField>

  <ParamField body="response" type="object">
    Information about the LLM's response (empty for beforeRequestHook)

    <Expandable title="Properties">
      <ParamField body="json" type="object">OpenAI compliant response body json.</ParamField>
      <ParamField body="text" type="string">Last message/prompt content from the overall response body.</ParamField>
      <ParamField body="statusCode" type="number">HTTP status code from LLM provider</ParamField>
    </Expandable>
  </ParamField>

  <ParamField body="provider" type="string">Portkey provider slug. Example: `openai`, `azure-openai`, etc.</ParamField>
  <ParamField body="requestType" type="string">Type of request: `chatComplete`, `complete`, or `embed`</ParamField>
  <ParamField body="metadata" type="object">Custom metadata passed with the request. Can come from: 1) the `x-portkey-metadata` header, 2) default API key settings, or 3) workspace defaults.</ParamField>
  <ParamField body="eventType" type="string">When the hook is triggered: `beforeRequestHook` or `afterRequestHook`</ParamField>
</Expandable>

#### Event Types

Your webhook can be triggered at two points:

* **beforeRequestHook**: Before the request is sent to the LLM provider
* **afterRequestHook**: After receiving a response from the LLM provider

<CodeGroup>
  ```JSON beforeRequestHook Example [expandable] theme={"system"}
   {
      "request": {
          "json": {
              "stream": false,
              "messages": [
                  {
                      "role": "system",
                      "content": "You are a helpful assistant"
                  },
                  {
                      "role": "user",
                      "content": "Say Hi"
                  }
              ],
              "max_tokens": 20,
              "n": 1,
              "model": "gpt-4o-mini"
          },
          "text": "Say Hi",
          "isStreamingRequest": false,
          "isTransformed": false
      },
      "response": {
          "json": {},
          "text": "",
          "statusCode": null,
          "isTransformed": false
      },
      "provider": "openai",
      "requestType": "chatComplete",
      "metadata": {
          "_user": "visarg123"
      },
      "eventType": "beforeRequestHook"
  }
  ```

  ```JSON afterRequestHook Example [expandable] theme={"system"}
  {
      "request": {
          "json": {
              "stream": false,
              "messages": [
                  {
                      "role": "system",
                      "content": "You are a helpful assistant"
                  },
                  {
                      "role": "user",
                      "content": "Say Hi"
                  }
              ],
              "max_tokens": 20,
              "n": 1,
              "model": "gpt-4o-mini"
          },
          "text": "Say Hi",
          "isStreamingRequest": false,
          "isTransformed": false
      },
      "response": {
          "json": {
              "id": "chatcmpl-B9SAAj7zd4mq12omkeEImYvYnjbOr",
              "object": "chat.completion",
              "created": 1741592910,
              "model": "gpt-4o-mini-2024-07-18",
              "choices": [
                  {
                      "index": 0,
                      "message": {
                          "role": "assistant",
                          "content": "Hi! How can I assist you today?",
                          "refusal": null
                      },
                      "logprobs": null,
                      "finish_reason": "stop"
                  }
              ],
              "usage": {
                  "prompt_tokens": 18,
                  "completion_tokens": 10,
                  "total_tokens": 28,
                  "prompt_tokens_details": {
                      "cached_tokens": 0,
                      "audio_tokens": 0
                  },
                  "completion_tokens_details": {
                      "reasoning_tokens": 0,
                      "audio_tokens": 0,
                      "accepted_prediction_tokens": 0,
                      "rejected_prediction_tokens": 0
                  }
              },
              "service_tier": "default",
              "system_fingerprint": "fp_06737a9306"
          },
          "text": "Hi! How can I assist you today?",
          "statusCode": 200,
          "isTransformed": false
      },
      "provider": "openai",
      "requestType": "chatComplete",
      "metadata": {
          "_user": "visarg123"
      },
      "eventType": "afterRequestHook"
  }
  ```
</CodeGroup>

### Webhook Response Structure

Your webhook must return a response that follows this structure:

#### Response Body

<Expandable title="Webhook Response Schema">
  <ResponseField required name="verdict" type="boolean">
    Whether the request/response passes your guardrail check:

    * `true`: No violations detected
    * `false`: Violations detected
  </ResponseField>

  <ResponseField name="transformedData" type="object">
    Optional field to modify the request or response

    <Expandable title="Properties">
      <ResponseField name="request" type="object">
        Modified request data (only for beforeRequestHook)
        If this field is found in the Webhook response, Portkey will fully override the existing request body with the returned data.
      </ResponseField>

      <ResponseField name="response" type="object">
        Modified response data (only for afterRequestHook)
        If this field is found in the Webhook response, Portkey will fully override the existing response body with the returned data.
      </ResponseField>
    </Expandable>
  </ResponseField>
</Expandable>

## Webhook Capabilities

Your webhook can perform three main actions:

### Simple Validation

Return a verdict without modifying the request/response:

```json theme={"system"}
{
  "verdict": true  // or false if the request violates your guardrails
}
```

### Request Transformation

Modify the user's request before it reaches the LLM provider:

<Tabs>
  <Tab title="Example: Adding Content Policy">
    ```json theme={"system"}
    {
      "verdict": true,
      "transformedData": {
        "request": {
          "json": {
            "messages": [
              {
                "role": "system",
                "content": "You are a helpful assistant. Do not provide harmful content."
              },
              {
                "role": "user",
                "content": "Original user message"
              }
            ],
            "max_tokens": 100,
            "model": "gpt-4o"
          }
        }
      }
    }
    ```
  </Tab>

  <Tab title="Example: PII Redaction">
    ```json theme={"system"}
    {
      "verdict": true,
      "transformedData": {
        "request": {
          "json": {
            "messages": [
              {
                "role": "system",
                "content": "You are a helpful assistant"
              },
              {
                "role": "user",
                "content": "My name is [REDACTED] and my email is [REDACTED]"
              }
            ],
            "max_tokens": 100,
            "model": "gpt-4o"
          }
        }
      }
    }
    ```
  </Tab>
</Tabs>

### Response Transformation

Modify the LLM's response before it reaches the user:

<Tabs>
  <Tab title="Example: Content Filtering">
    ```json theme={"system"}
    {
      "verdict": true,
      "transformedData": {
        "response": {
          "json": {
            "id": "chatcmpl-123",
            "object": "chat.completion",
            "created": 1741592832,
            "model": "gpt-4o-mini",
            "choices": [
              {
                "index": 0,
                "message": {
                  "role": "assistant",
                  "content": "I've filtered this response to comply with our content policies."
                },
                "finish_reason": "stop"
              }
            ],
            "usage": {
              "prompt_tokens": 23,
              "completion_tokens": 12,
              "total_tokens": 35
            }
          },
          "text": "I've filtered this response to comply with our content policies."
        }
      }
    }
    ```
  </Tab>

  <Tab title="Example: Response Enhancement">
    ```json theme={"system"}
    {
      "verdict": true,
      "transformedData": {
        "response": {
          "json": {
            "id": "chatcmpl-123",
            "object": "chat.completion",
            "created": 1741592832,
            "model": "gpt-4o-mini",
            "choices": [
              {
                "index": 0,
                "message": {
                  "role": "assistant",
                  "content": "Original response with additional disclaimer: This response is provided for informational purposes only."
                },
                "finish_reason": "stop"
              }
            ],
            "usage": {
              "prompt_tokens": 23,
              "completion_tokens": 20,
              "total_tokens": 43
            }
          },
          "text": "Original response with additional disclaimer: This response is provided for informational purposes only."
        }
      }
    }
    ```
  </Tab>
</Tabs>

## Passing Metadata to Your Webhook

You can include additional context with each request using Portkey's metadata feature:

```json theme={"system"}
// In your API request to Portkey
"x-portkey-metadata": {"user": "john", "context": "customer_support"}
```

This metadata will be forwarded to your webhook in the `metadata` field. [Learn more about metadata](/product/observability/metadata).

## Important Implementation Notes

1. **Complete Transformations**: When using `transformedData`, include all fields in your transformed object, not just the changed portions.

2. **Independent Verdict and Transformation**: The `verdict` and any transformations are independent. You can return `verdict: false` while still returning transformations.

3. **Default Behavior**: If your webhook fails to respond within the timeout period, Portkey will default to `verdict: true`.

4. **Event Type Awareness**: When implementing transformations, ensure your webhook checks the `eventType` field to determine whether it's being called before or after the LLM request.

## Example Implementation

Check out our Guardrail Webhook implementation on GitHub:

<Card title="Guardrail Webhook Implementation" icon="github" href="https://github.com/Portkey-AI/gateway/blob/main/plugins/default/webhook.ts" />

## Get Help

Building custom webhooks? Join the [Portkey Discord community](https://portkey.ai/community) for support and to share your implementation experiences!
