# How to Use Hog Functions for Data Transformations and CDP in PostHog

> Learn how to use Hog Functions for real-time data transformations and CDP in PostHog. Unlock custom code, external destinations, and site apps without extra infrastructure.

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

---

**Hog Functions provide a unified REST API to run custom Hog or TypeScript code on events, enabling real-time transformations, external destinations, and site apps without deploying separate infrastructure.**

PostHog's Hog Functions system, implemented in the `PostHog/posthog` repository, offers a serverless approach to Customer Data Platform (CDP) operations. This architecture allows teams to mutate, enrich, and forward event data using either Hog or TypeScript code executed directly within the PostHog pipeline through three distinct use cases: **Destinations**, **Transformations**, and **Site Apps**.

## Understanding the Hog Functions Architecture

Hog Functions consolidate CDP logic into `HogFunction` model objects managed via the REST API at `/api/projects/@current/hog_functions/`. The system handles compilation, validation, and runtime execution through several specialized components.

**Core implementation files:**

- **[`posthog/models/hog_functions/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/models/hog_functions/hog_function.py)** – Defines the database model, handles file-system synchronization, and manages status polling through the `HogFunction` class.
- **[`posthog/api/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/api/hog_function.py)** – Contains the DRF viewset and `HogFunctionSerializer`, which validates code size, compiles Hog via `compile_hog`, and transpiles TypeScript via `get_transpiled_function`.
- **[`posthog/cdp/validation.py`](https://github.com/PostHog/posthog/blob/main/posthog/cdp/validation.py)** – Provides the `compile_hog` utility and template bytecode generation.
- **[`posthog/plugins/plugin_server_api.py`](https://github.com/PostHog/posthog/blob/main/posthog/plugins/plugin_server_api.py)** – Manages communication with the plugin server for reloading functions and testing invocations.

When a function is saved, the `hog_function_saved` signal (lines 49-54 in the model file) automatically reloads the function on the plugin server for relevant types, ensuring zero-downtime updates.

## Creating a Transformation Function

Transformations mutate or enrich events before they continue through the pipeline. To create one, send a POST request to the Hog Functions endpoint.

**Minimal required payload:**

```json
{
  "type": "transformation",
  "name": "Add GeoIP data",
  "hog": "function addGeoIP(event) { /* Hog code */ }",
  "enabled": true,
  "execution_order": 1,
  "inputs_schema": [],
  "filters": {}
}

```

**Validation and compilation process:**

1. The `HogFunctionSerializer.validate_type` method (lines 55-68 in [`posthog/api/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/api/hog_function.py)) ensures the type is `"transformation"`.
2. If `execution_order` is omitted, the serializer calls `_get_highest_execution_order` (lines 86-94) to assign the next highest integer.
3. During validation, the serializer invokes `compile_hog` from [`posthog/cdp/validation.py`](https://github.com/PostHog/posthog/blob/main/posthog/cdp/validation.py) to compile the Hog code.
4. The model's `save` method triggers `compile_filters_bytecode` (lines 37-44 in [`posthog/models/hog_functions/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/models/hog_functions/hog_function.py)) for non-JavaScript sources.

## Using Built-in Templates

PostHog ships with pre-defined templates that can be instantiated without writing code from scratch. The **GeoIP** transformation, for example, automatically enriches events with location data.

To create a function from a template:

```json
{
  "template_id": "template-geoip",
  "type": "transformation",
  "enabled": true
}

```

The serializer loads the template via `HogFunctionTemplate.get_template` and copies its code, icon, and input schema. New teams automatically receive default transformations through the `enabled_default_hog_functions_for_new_team` signal (lines 111-150 in the model file).

## Testing Functions Before Production

Always test transformations before enabling them on live traffic. Use the invocations endpoint to simulate execution.

**API endpoint:**

```

POST /api/projects/@current/hog_functions/<function_id>/invocations/

```

**Test payload structure:**

```json
{
  "configuration": {
    "type": "transformation",
    "hog": "function addGeoIP(event) { /* … */ }",
    "enabled": false,
    "filters": {}
  },
  "globals": {
    "event": { "event": "$pageview", "properties": {} }
  },
  "clickhouse_event": {
    "event": "$pageview",
    "properties": {}
  }
}

```

The `HogFunctionViewSet.invocations` method forwards the payload to the plugin server via `create_hog_invocation_test` (lines 51-68 in the API file). The response includes `status`, execution `logs`, and an optional `invocation_id` for debugging.

## Managing Execution Order for Multiple Transformations

When multiple transformation functions are active, the `execution_order` field determines their sequence. Lower numbers execute first.

To bulk update the order of several functions:

**Endpoint:**

```

PATCH /api/projects/@current/hog_functions/rearrange/

```

**Request body:**

```json
{
  "orders": {
    "c8a2f7e3-5c4b-4d2a-9b8e-1f5d2a6b9c3d": 1,
    "e7d3b9a1-2f6c-4b8d-8e5a-0c9e1a2d3f4b": 2
  }
}

```

The viewset validates that all IDs belong to the requesting team, updates the `execution_order` fields, writes an activity log entry, and returns the newly ordered list.

## Enabling Historic Backfills for Destinations

For **destination** functions (not transformations), you can replay historic events by attaching a Batch Export backfill workflow.

**Endpoint:**

```

POST /api/projects/@current/hog_functions/<function_id>/enable_backfills/

```

This endpoint constructs a Batch Export configuration, validates the "backfill-workflows-destination" feature flag, and stores the resulting export ID on the function object (lines 84-112 in [`posthog/api/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/api/hog_function.py)).

## Complete Implementation Example

The following cURL commands demonstrate the full lifecycle of a transformation function:

**1. Create the transformation:**

```bash
curl -X POST https://app.posthog.com/api/projects/@current/hog_functions/ \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
        "type":"transformation",
        "name":"Upper-case event name",
        "hog":"function uppercase(event) { event.event = event.event.toUpperCase(); return event; }",
        "enabled":false,
        "execution_order":1
      }'

```

**2. Test with sample data:**

```bash
curl -X POST https://app.posthog.com/api/projects/@current/hog_functions/<id>/invocations/ \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
        "configuration": {
          "type":"transformation",
          "hog":"function uppercase(event){event.event=event.event.toUpperCase();return event;}"
        },
        "globals": {"event": {"event":"$pageview","properties":{}}}
      }'

```

**3. Enable after successful testing:**

```bash
curl -X PATCH https://app.posthog.com/api/projects/@current/hog_functions/<id>/ \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

```

## Summary

- **Hog Functions** unify transformations, destinations, and site apps into a single serverless system accessed via `/api/projects/@current/hog_functions/`.
- The `HogFunctionSerializer` automatically validates type constraints, assigns `execution_order`, and compiles Hog code using `compile_hog` from [`posthog/cdp/validation.py`](https://github.com/PostHog/posthog/blob/main/posthog/cdp/validation.py).
- **Test** functions using the `/invocations/` endpoint and the `create_hog_invocation_test` utility before enabling them in production.
- **Reorder** chained transformations via the `/rearrange/` endpoint to control mutation sequence.
- **Backfill** historic data for destinations using the `/enable_backfills/` endpoint, which creates Batch Export configurations.
- All state changes trigger the `hog_function_saved` signal to reload the plugin server without manual intervention.

## Frequently Asked Questions

### What is the difference between a transformation and a destination in Hog Functions?

A **transformation** mutates or enriches the event object itself and passes the modified event back into the pipeline for further processing. A **destination** forwards a copy of the event to an external service (like a webhook or data warehouse) without altering the original event flow. Transformations use the `execution_order` field to sequence multiple mutations, while destinations support historic backfills via the `/enable_backfills/` endpoint.

### How do I debug a Hog Function that is not processing events?

Use the `/invocations/` test endpoint to simulate event processing and inspect the returned `logs` array. Check the function's status in the `HogFunction` model and verify that the `hog_function_saved` signal fired correctly to reload the plugin server. For runtime issues, examine the `compile_hog` output in [`posthog/cdp/validation.py`](https://github.com/PostHog/posthog/blob/main/posthog/cdp/validation.py) to ensure your code compiles without errors.

### Can I write Hog Functions in TypeScript instead of Hog?

Yes. For site apps and specific use cases, the system supports TypeScript through the `get_transpiled_function` method in [`posthog/cdp/site_functions.py`](https://github.com/PostHog/posthog/blob/main/posthog/cdp/site_functions.py). The `HogFunctionSerializer` detects the language and routes the code accordingly, though transformations and destinations typically use Hog for execution within the plugin server.

### What happens if I don't specify an execution_order for a transformation?

The `HogFunctionSerializer` automatically assigns the next highest available integer via the `_get_highest_execution_order` method (lines 86-94 in [`posthog/api/hog_function.py`](https://github.com/PostHog/posthog/blob/main/posthog/api/hog_function.py)). This ensures the new transformation runs after all existing ones by default. To prioritize a transformation earlier in the chain, explicitly set a lower integer value or use the `/rearrange/` endpoint to reorder multiple functions atomically.