Incoming webhooks

Incoming webhooks enable you to send data from external systems directly into PostHog. This is useful for integrating with custom applications, third-party services, or any system that can make HTTP requests.

In general, we recommend using our SDKs or capture API but this is useful when you don't have control over the sender format and need to perform some transformation to match PostHog's event format.

How it works

When you create an incoming webhook source, PostHog generates a unique URL that you can use to send data from your external systems. The webhook accepts POST requests with any JSON payload or GET requests with query parameters, and you can configure how to parse and map the incoming data to PostHog events.

Webhook URL format

Your webhook URL will look like this:

https://webhooks.us.posthog.com/public/webhooks/<UUID>

Transforming the webhook payload

We recommend starting from the default HTTP incoming webhook template. For most use cases, you can transform the incoming webhook to the required properties simply using our Hog templating language.

The request object

The request object is available for templating the inputs as well as the underlying Hog code. It contains the following properties:

  • request.method: The HTTP method of the request
  • request.query: The query parameters of the request parsed as a dictionary of strings
  • request.body: The body of the request parsed as JSON
  • request.headers: The headers of the request as a dictionary
  • request.ip: The IP address of the requester

For example if you send a request with curl like so:

Terminal
curl -X POST https://webhooks.<region>.posthog.com/public/webhooks/<UUID>?test=123 \
-H "Content-Type: application/json" \
-d '{
"event": "my example event",
"distinct_id": "webhook-test-123"
}'

Then the variables available to use will look like:

Hog
print(request.method)
// Outputs: 'POST'
print(request.query)
// Outputs: { 'test': '123' }
print(request.headers)
// Outputs: { 'Content-Type': 'application/json' }
print(request.body)
// Outputs: { 'event': 'my example event', 'distinct_id': 'webhook-test-123' }
print(request.ip)
// Outputs: '127.0.0.1'

Capturing a PostHog event

If using the default template, you simply need to customize the inputs so that the required values event and distinct_id are set as well as the optional properties object.

Under the hood, the code to capture an event is:

Hog
postHogCapture({
'event': input.event,
'distinct_id': input.distinct_id,
'properties': input.properties
})

(Advanced) HTTP requests and background processing

Webhook sources can call postHogCapture to ingest events to PostHog. You can also do HTTP calls with fetch, but, in this case, the request is queued to a background task, a 201 Created response is returned, and the event is ingested asynchronously.

Troubleshooting

Testing your webhook

You can test your webhook by sending a request to the webhook URL. You can use a tool like curl to send a request or use our built in testing tools when setting up the webhook. If you use curl or another tool, you can turn on the Log payloads switch in the configuration tab, and then check the logs tab to see the webhooks come in.

Currently you have to save the webhook to test it as it directly hits the endpoint ensuring that the complete flow works.

Common issues

  • 401 Unauthorized: Check that your authorization header matches the configured value
  • 400 Bad Request: Verify that required fields (event and distinct_id) are provided
  • Timeout errors: Ensure your webhook processing completes within the time limit

Community questions

Was this page useful?

Questions about this page? or post a community question.