How to Implement Webhook Integrations with Hog Functions in PostHog

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, the HogFunctionType enum defines several function categories, but only WAREHOUSE_SOURCE_WEBHOOK supports automatic creation via data-warehouse integrations:

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 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. 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 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:

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, 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:

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 returns:

{
  "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:

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, 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.

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.

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, then wire it into the data-warehouse API endpoints to enable automatic Hog Function creation and external webhook management for your custom provider.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →