# Send Events From PostHog

> Send the conversions you already track in PostHog to Revnu, so your ads optimise on them without the Revnu Tag or the ad platforms' pixels.

Canonical: https://revnu.com/docs/posthog-events
Last updated: 2026-11-01

If you already track sign-ups, bookings and purchases in PostHog, PostHog can send each one to Revnu as it happens. Revnu ties it to the ad click that brought the person and passes it on to Google, LinkedIn, Reddit and your other ad platforms, so your campaigns learn from real conversions.

## Set it up in PostHog

1. Open **Data pipelines**, then **Destinations**, and add a new **HTTP Webhook** destination.
2. Set the webhook URL to `https://revnu.com/api/measurement/posthog` and the method to `POST`.
3. Leave the JSON body as PostHog's default: `{"event": "{event}", "person": "{person}"}`.
4. Under **Secret headers** (not **Headers**), add `Authorization` with the value `Bearer ` followed by your event key. Secret headers are encrypted and kept out of PostHog's logs. The key starts with `rsk_live_` and is in **Ads → Revnu Tag**, under **Install Details and Keys**.
5. Under the destination's filters, match only your conversion events, such as your sign-up, booked call and purchase events. Don't send page views.
6. Turn on **Log responses**, so Revnu's answer for each event, including any `not_matchable_reason`, shows in the destination's logs.
7. Enable the destination.

Revnu stores each event under its name in lowercase snake_case: `Signed Up` becomes `signed_up`, `checkout completed` becomes `checkout_completed`. Your win definition in Revnu has to count those names. The response's `event_name` shows the name Revnu stored.

## What Revnu reads from each event

| From PostHog | Used for |
|---|---|
| `event.uuid` | The event id. PostHog's retries of the same event are recorded once. |
| `event.event` | The event name, converted to snake_case. |
| `event.timestamp` | When it happened. |
| `utm_*` and click ids (`gclid`, `li_fat_id`, `rdt_cid`, `fbclid`, `msclkid`, `ttclid`, `twclid`, `gbraid`, `wbraid`) on the event, its `$current_url`, or its session's landing page (`$session_entry_*`) | Which ad click and campaign brought the person. |
| The person's `email` (or `$set.email`) and `phone` | Hashed on arrival, then used to match the person on your ad platforms. |
| `event.distinct_id` | Hashed. Joins a later event, such as a payment captured on your server, to the same person's earlier sign-up. |
| `revenue` or `value`, with `currency` | The conversion's value, in whole currency units. Read on every event, not only the first purchase. |
| `reversal_of` | The `uuid` of an earlier event this one cancels. See [Cancellations and refunds](#cancellations-and-refunds). |
| `attribution_captured_at` | When the click on this event was first seen, if you save attribution yourself. See [If you save your own attribution](#if-you-save-your-own-attribution). |
| Your own event properties | Kept for your win definition's rules. PostHog's `$` properties, emails, names and IP addresses are dropped. |

Revnu never stores a raw email, phone, name, IP address or distinct id from PostHog.

## Make every conversion matchable

An ad platform can only use a conversion that carries the click id from the ad, or the person's email or phone. To make sure yours do:

- Identify people in PostHog with their email (`posthog.identify(userId, { email })`) when they sign up.
- Capture sign-ups and bookings in the browser, or in the same session as the ad click, so the landing page's click id rides along.
- Capture server-side events, such as Stripe payments, with the same `distinct_id` you identify the person with in the browser.

A conversion with no click id, email or phone is still recorded, and the response includes `not_matchable_reason`.

## If you save your own attribution

Some sites save the UTMs and click ids from the landing page and put them on every later event, so a purchase weeks after the click still carries them. That works with Revnu if you also:

- Save `revnu_campaign_id` from the landing page URL along with your UTMs, as an event property of that name. Revnu adds it to every ad it runs, and it is how a conversion is tied to the campaign. Without it, Revnu falls back to the session's landing page or the person's earlier events.
- Send `attribution_captured_at`, the time the click was first seen (ISO 8601, or Unix milliseconds as a number or a string of digits). Without it, Revnu treats the click as happening when the event did, and a click older than your win definition's click window would still get credit.

## Cancellations and refunds

Send a cancellation or refund as its own event, with a `reversal_of` property set to the `uuid` of the event it cancels. If the original carried `revenue` and `currency`, repeat the same values; Revnu only takes back a whole conversion, never part of one.

When a reversal arrives:

- Revnu stops counting the original in your results.
- Google Ads gets a retraction, so the conversion is removed there too. Google can't retract a conversion matched only on `wbraid`; Revnu flags those for review instead.
- LinkedIn, Reddit, X and Microsoft have no way to take a conversion back once it is sent, so it stays counted on those platforms.

A cancellation without `reversal_of` is recorded as its own event and leaves the original counted.

If cancellations are common and you run ads beyond Google, consider counting the later, firmer event (a held call rather than a booked one) as your win.

## Where each conversion goes

Revnu sends a conversion to the ad platform whose Revnu campaign brought the person, within your win definition's click window (1 to 90 days). Conversions from campaigns you run yourself, outside Revnu, are not sent.

| Platform | How Revnu sends it | What it matches on |
|---|---|---|
| Google Ads | Offline click conversion import, into a conversion action Revnu creates | The click id (`gclid`, `gbraid` or `wbraid`). Email is not sent to Google |
| LinkedIn | Conversions API, on a conversion rule Revnu creates | `li_fat_id` and the hashed email |
| Reddit | Conversions API, on your ad account's pixel | `rdt_cid` and the hashed email |
| X | Conversions API, on a conversion event Revnu creates | `twclid` and the hashed email |
| Microsoft Advertising | Offline conversion import, into a goal Revnu creates | `msclkid` |

When a conversion carries both a click id and an email, Revnu sends both and the platform matches on whichever it can. Conversions that land days or weeks after the click are fine, as long as they are inside the click window.

Every outcome in your win definition is sent with its own value, so an upgrade event that carries the increase in `revenue` reports expansion revenue. Campaigns bid on the outcome they were launched for; later outcomes are reported alongside it.

## Response

Same as the [Event API](https://revnu.com/docs/event-api.md#response), plus:

- `not_matchable_reason` appears when the event carried no ad click id and no email or phone, and wasn't joined to the person's earlier event, so no ad platform can match it.

| Code | Meaning |
|---|---|
| 200 | Recorded and credited to one of your Revnu ad campaigns, or a retry of an event already recorded |
| 202 | Recorded, not credited to a campaign |
| 400 | The body is not PostHog's webhook body. Restore the destination's default JSON body |
| 401 | Missing or invalid event key in the `Authorization` header |
| 413 | Body over 64 KB |
| 422 | A field failed validation; `error` says which |
| 429 | Rate limited. PostHog retries |

## Frequently asked questions

### Do I still need the Revnu Tag or the ad platforms' pixels?

No. PostHog sends the conversions, and Revnu sends them to your ad platforms through their conversion APIs. The Revnu Tag adds visit-level reporting on top, but ads can optimise without it.

### Do I need to turn anything on in my ad accounts?

No. Connect the ad accounts to Revnu. Revnu creates the conversion actions, rules and events it sends to. Google's Enhanced Conversions and its customer data terms are not needed, because Revnu sends Google the click id, not the email.

### Why does my conversion say not_matchable_reason?

PostHog sent it without an ad click id and without the person's email or phone. Identify people with their email in PostHog, and capture the event in the browser or with the same `distinct_id` as the person's browser events.

### Will PostHog's test button create a real event?

Only if you turn off mocking in the test panel. A real test request is recorded like any other conversion.
