# How to Implement Webhook Integrations with Hog Functions in PostHog

> Learn to implement webhook integrations with PostHog Hog Functions. Trigger custom logic with inbound webhook payloads using warehouse_source_webhook for seamless event handling.

- Repository: [PostHog/posthog](https://github.com/PostHog/posthog)
- Tags: how-to-guide
- Published: 2026-04-25

---

**PostHog's Hog Functions provide a unified way to run custom logic in response to events, including inbound webhook payloads using the `warehouse_source_webhook` type.**

The PostHog/posthog repository enables real-time data ingestion through webhook integrations with Hog Functions, allowing teams to process external events and route them automatically into the data warehouse. These integrations leverage a specialized function type designed specifically for inbound webhooks, orchestrated through a layered architecture of models, templates, and service utilities. Understanding how to implement webhook integrations with Hog Functions unlocks the ability to build custom data pipelines that respond instantly to third-party events.

## Understanding Hog Function Types for Webhooks

In [`posthog/models/hog_functions/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/models/hog_functions/hog_function.py), the `HogFunctionType` enum defines several function categories, but only `WAREHOUSE_SOURCE_WEBHOOK` supports automatic creation via data-warehouse integrations:

```python
class HogFunctionType(models.TextChoices):
    DESTINATION = "destination"
    SITE_DESTINATION = "site_destination"
    INTERNAL_DESTINATION = "internal_destination"
    SOURCE_WEBHOOK = "source_webhook"
    WAREHOUSE_SOURCE_WEBHOOK = "warehouse_source_webhook"   # ← Inbound data imports

    SITE_APP = "site_app"
    TRANSFORMATION = "transformation"

```

According to the validation logic in `HogFunctionSerializer.validate_type`, you cannot create `warehouse_source_webhook` functions directly through the public Hog Function API. These must be instantiated through the data-warehouse service layer to ensure proper external webhook registration and security configuration.

## Webhook Integration Architecture

PostHog implements webhook integrations through three distinct layers that handle function definition, template management, and external orchestration.

### Data Model Layer

The `HogFunction` model in [`posthog/models/hog_functions/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/models/hog_functions/hog_function.py) stores the generated code, input schemas, and runtime configuration. For webhook integrations, this model captures critical fields including the `type`, `team` association, and encrypted `inputs` containing schema mappings and signing secrets.

### Template System

Templates declaratively define webhook behavior in [`posthog/models/hog_function_template.py`](https://github.com/PostHog/posthog/blob/main/posthog/models/hog_function_template.py). Each template specifies the Hog code to execute, the `inputs_schema` for UI rendering, and default configurations. The warehouse source webhook template determines how inbound payloads map to internal data schemas before storage.

### Service Orchestration Layer

The [`products/data_warehouse/backend/external_data_source/webhooks.py`](https://github.com/PostHog/posthog/blob/main/products/data_warehouse/backend/external_data_source/webhooks.py) file contains the high-level orchestration logic that coordinates between PostHog and external providers. This layer manages the complete lifecycle: creating the Hog Function, registering the external webhook URL, and handling deletion cleanup.

## Implementing Webhook Creation Programmatically

To create a webhook-driven data import, use the `get_or_create_webhook_hog_function` utility. This function performs an upsert operation that preserves existing schema mappings while adding new ones:

```python
from posthog.models import Team
from posthog.temporal.data_imports.sources.stripe.source import StripeSource
from posthog.temporal.data_imports.sources.common.config import Config
from products.data_warehouse.backend.external_data_source.webhooks import (
    get_or_create_webhook_hog_function,
    create_and_register_webhook,
)

team = Team.objects.get(id=42)                     # Your PostHog team

source = StripeSource()                           # Any WebhookSource subclass

config = Config(api_key="sk_test_…")              # Provider-specific configuration

source_id = "acct_1ABCDEF"                        # External account identifier

eligible_schemas = [...]                          # List of ExternalDataSchema objects

# 1. Create or update the Hog Function and retrieve its public URL

hog_res = get_or_create_webhook_hog_function(
    team=team,
    source=source,
    source_id=source_id,
    eligible_schemas=eligible_schemas,
)
print("Hog Function ID:", hog_res.hog_function.id)
print("Public webhook URL:", hog_res.webhook_url)

# 2. Register the external webhook (calls Stripe's API)

setup = create_and_register_webhook(
    source=source,
    config=config,
    hog_fn_result=hog_res,
    team_id=team.id,
)

if setup.success:
    print("External webhook created successfully")
else:
    print("Failed to create webhook:", setup.error)

```

The `get_or_create_webhook_hog_function` implementation merges existing `schema_mapping` values to prevent overwriting previous configurations when adding new schemas. It returns a `WebhookHogFunctionCreateResult` containing the function instance, a creation flag, and the public webhook URL generated by `get_webhook_url`.

## Registering External Webhooks

After creating the Hog Function, `create_and_register_webhook` handles external provider communication. This function:

1. Calls the source-specific `source.create_webhook` implementation
2. Captures any extra inputs returned by the external provider (such as signing secrets)
3. Persists these credentials back to the Hog Function's `inputs` field for runtime payload verification

For example, when integrating with Stripe via [`posthog/temporal/data_imports/sources/stripe/source.py`](https://github.com/PostHog/posthog/blob/main/posthog/temporal/data_imports/sources/stripe/source.py), the implementation creates the webhook through Stripe's API and returns the webhook secret, which PostHog stores securely to validate incoming signatures.

## REST API Endpoints for Webhook Management

The data-warehouse REST API exposes endpoints for webhook lifecycle management without requiring direct Hog Function API access.

### Creating Webhooks via REST

Send a POST request to create both the Hog Function and external webhook registration:

```http
POST /api/environments/42/external_data_sources/7/create_webhook/
Content-Type: application/json

{
  "config": {
    "api_key": "sk_test_…"
  },
  "source_id": "acct_1ABCDEF",
  "schemas": [ "customers", "charges" ]
}

```

The endpoint defined in [`products/data_warehouse/backend/api/external_data_source.py`](https://github.com/PostHog/posthog/blob/main/products/data_warehouse/backend/api/external_data_source.py) returns:

```json
{
  "hog_function_id": "123e4567-e89b-12d3-a456-426655440000",
  "webhook_url": "https://webhooks.us.posthog.com/public/webhooks/dwh/123e4567-e89b-12d3-a456-426655440000",
  "created": true
}

```

### Deleting Webhook Integrations

To remove a webhook integration, call the delete endpoint:

```http
POST /api/environments/42/external_data_sources/7/delete_webhook/
Content-Type: application/json

{
  "source_id": "acct_1ABCDEF"
}

```

The `delete_webhook_and_hog_function` service function soft-deletes the Hog Function by setting `deleted=True` and `enabled=False`, while simultaneously calling `source.delete_webhook` to clean up the external provider registration.

## Webhook Data Flow

When an external provider sends a payload to your generated webhook URL, the data flows through [`products/data_warehouse/backend/webhook_consumer/writer.py`](https://github.com/PostHog/posthog/blob/main/products/data_warehouse/backend/webhook_consumer/writer.py), which writes incoming payloads to Parquet files in S3. The Hog Function executes during this ingestion pipeline, applying transformations defined in the template code before the data reaches your warehouse tables.

## Summary

- **Use `WAREHOUSE_SOURCE_WEBHOOK`** as the function type for inbound data integrations; direct API creation is blocked to ensure proper external registration.
- **Leverage `get_or_create_webhook_hog_function`** to handle Hog Function upserts with automatic schema mapping preservation.
- **Call `create_and_register_webhook`** to coordinate external provider API calls and capture security credentials like signing secrets.
- **Access the REST endpoints** at `/api/environments/<team>/external_data_sources/<source>/create_webhook/` for no-code webhook setup.
- **Soft-delete via `delete_webhook_and_hog_function`** to cleanly remove both the external webhook and internal Hog Function without data loss.

## Frequently Asked Questions

### What is the difference between `SOURCE_WEBHOOK` and `WAREHOUSE_SOURCE_WEBHOOK`?

`SOURCE_WEBHOOK` is a generic type for basic webhook ingestion, while `WAREHOUSE_SOURCE_WEBHOOK` is specifically designed for data-warehouse integrations that require automatic external webhook registration, schema mapping, and credential management. Only `WAREHOUSE_SOURCE_WEBHOOK` functions can be created through the data-warehouse service layer in [`products/data_warehouse/backend/external_data_source/webhooks.py`](https://github.com/PostHog/posthog/blob/main/products/data_warehouse/backend/external_data_source/webhooks.py).

### Why can't I create a `warehouse_source_webhook` function through the standard Hog Function API?

The `HogFunctionSerializer.validate_type` method explicitly blocks direct creation of `warehouse_source_webhook` functions through the public API. This restriction ensures that external webhooks are properly registered with the provider and that security credentials like signing secrets are securely captured and stored in the function's encrypted inputs field.

### How does PostHog validate incoming webhook payloads?

When external providers return security credentials during `create_and_register_webhook`, these values are persisted in the Hog Function's `inputs` field. At runtime, the function template code accesses these inputs to verify webhook signatures—such as Stripe's signature verification—before processing the payload and writing it to S3 via [`products/data_warehouse/backend/webhook_consumer/writer.py`](https://github.com/PostHog/posthog/blob/main/products/data_warehouse/backend/webhook_consumer/writer.py).

### Can I implement custom webhook integrations for providers not natively supported?

Yes, you can extend the system by implementing a `WebhookSource` subclass that provides `create_webhook`, `delete_webhook`, and a template reference. Place your implementation following the pattern in [`posthog/temporal/data_imports/sources/stripe/source.py`](https://github.com/PostHog/posthog/blob/main/posthog/temporal/data_imports/sources/stripe/source.py), then wire it into the data-warehouse API endpoints to enable automatic Hog Function creation and external webhook management for your custom provider.