PostHog

Datafly Signal delivers events to PostHog server-to-server using the capture API (/i/v0/e/). Events are sent directly from your Signal deployment to PostHog, so delivery is unaffected by ad blockers, browser tracking prevention, or client-side script failures.

Why route PostHog through Datafly Signal

  • Your numbers stop depending on what the browser allows. Client-side analytics loses every visit where the tag is blocked or the page unloads early. Signal delivers from the server, so PostHog receives the complete event stream.
  • Client IP and user agent are carried in the payload. Both are taken from the original visitor request rather than your server, which is what PostHog needs to attribute the event to the right session.
  • One consent decision governs every destination. PostHog is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.

Prerequisites

Before configuring PostHog in Signal, you need a PostHog project and its Project API Key.

Create or open a PostHog project

  1. Sign in at PostHog (US Cloud) or eu.posthog.com (EU Cloud), or open your self-hosted instance.
  2. Create a project if you don’t already have one.

Find your Project API Key

  1. Go to Settings > Project.
  2. Copy the Project API Key (a write-only token that starts with phc_).

The Project API Key is a public, write-only token. It can only send events — it cannot read data from your project — so it is safe to use as the capture credential.

Note your data region

PostHog Cloud runs in two independent regions. Your project lives in exactly one of them:

  • US Cloud — ingestion host us.i.posthog.com
  • EU Cloud — ingestion host eu.i.posthog.com

For self-hosted PostHog, use your own instance host.

⚠️

The region must match the project. A US token sent to the EU host (or vice versa) is silently rejected — events return a success-shaped response but never appear in your project.

Configure in Signal

FieldRequiredDescription
project_api_keyYesYour PostHog Project API Key (starts with phc_). Settings > Project > Project API Key.
ingestion_hostYesYour data region: us.i.posthog.com, eu.i.posthog.com, or your self-hosted host.

Management UI Setup

  1. Go to Integrations > Add Integration > PostHog.
  2. Enter your project_api_key.
  3. Select your Ingestion Host (data region).
  4. Select consent categories (typically analytics).
  5. Click Save.

API Endpoint

POST https://us.i.posthog.com/i/v0/e/      # US Cloud
POST https://eu.i.posthog.com/i/v0/e/      # EU Cloud

Each event is sent as a single JSON object. The Project API Key travels in the request body as api_key — there is no Authorization header.

{
  "api_key": "phc_xxxxxxxxxxxxxxxxxxxx",
  "event": "$pageview",
  "distinct_id": "user-123",
  "properties": {
    "$current_url": "https://example.com/pricing"
  },
  "timestamp": "2026-06-06T12:00:00.000Z"
}

Identity Signals

PostHog identifies users by a distinct_id that must be present on every event.

SignalFieldDescription
distinct_iddistinct_idThe logged-in user ID (user_id) when available, falling back to Signal’s anonymous_id for anonymous traffic.
Anonymous IDproperties.$anon_distinct_idSignal’s anonymous_id, so PostHog can stitch pre-login activity to the identified person.
IP addressproperties.$ipVisitor IP, forwarded from the original request, so PostHog resolves GeoIP server-side.
Libraryproperties.$libIdentifies the sending library.

Person properties

When a user is identified via datafly.identify() with traits, Signal attaches them to PostHog’s person profile using PostHog’s $set and $set_once conventions:

TraitTargetBehaviour
emailproperties.$set.email$set overwrites on every event.
nameproperties.$set.name$set overwrites on every event.
phoneproperties.$set.phone$set overwrites on every event.
email (initial)properties.$set_once.initial_email$set_once is written only the first time.

PostHog stores person properties as raw values (it has no hashed-PII matching model). Signal sends email, name, and phone in clear under $set. Delivery is gated by the pipeline’s consent categories — events are only sent when the visitor’s consent allows the configured category (typically analytics).

datafly.identify("user-123", {
  email: "jane.doe@example.com",
  name: "Jane Doe",
  phone: "+44 7700 900123"
});

Event Mapping

Signal’s GA4-style event names are mapped to PostHog snake_case event names. PostHog’s reserved $pageview event powers its web-analytics paths and referrers report.

Signal eventPostHog event
page$pageview
Signed Upsigned_up
Logged Inlogged_in
Products Searchedproducts_searched
Product Viewedproduct_viewed
Product Addedproduct_added
Checkout Startedcheckout_started
Order Completedorder_completed

Any event not in the map is dropped (the blueprint’s default action is drop). To customise, edit the integration’s Field Mappings in the Management UI.

Example: Order Completed → order_completed

Datafly.js call:

datafly.track("Order Completed", {
  order_id: "ORD-001",
  revenue: 129.99,
  currency: "USD",
  products: [
    { product_id: "SKU-A", name: "Widget", price: 49.99, quantity: 2 },
    { product_id: "SKU-B", name: "Gadget", price: 30.01, quantity: 1 }
  ]
});

PostHog payload sent by Signal:

{
  "api_key": "phc_xxxxxxxxxxxxxxxxxxxx",
  "event": "order_completed",
  "distinct_id": "user-123",
  "timestamp": "2026-06-06T12:00:00.000Z",
  "properties": {
    "$ip": "203.0.113.50",
    "$lib": "datafly-js",
    "$anon_distinct_id": "01HX8…",
    "order_id": "ORD-001",
    "revenue": 129.99,
    "currency": "USD",
    "products": [
      { "product_id": "SKU-A", "name": "Widget", "price": 49.99, "quantity": 2 },
      { "product_id": "SKU-B", "name": "Gadget", "price": 30.01, "quantity": 1 }
    ],
    "$set": { "email": "jane.doe@example.com", "name": "Jane Doe" }
  }
}

Testing Your Integration

Trigger events

Browse your site or call datafly.track() / datafly.identify() to generate events.

Watch the PostHog Activity view

  1. In PostHog, open Activity (the live events feed).
  2. New events should appear within seconds, tagged with their distinct_id and properties.

Verify person properties

  1. Open People and search for the distinct_id.
  2. Confirm email / name appear under the person’s properties after an identify call.

Check web analytics

  1. Open Web analytics.
  2. Confirm page views populate paths and referrers — these come from $pageview with $current_url and $referrer.

Troubleshooting

ProblemSolution
Events not appearingConfirm the ingestion host matches the project’s region (US token to US host, EU to EU). A region mismatch is silently rejected.
Events accepted but person not identifiedEnsure distinct_id is stable — events for the same user must share the same distinct_id. Check the user_idanonymous_id fallback resolves.
GeoIP / location missingConfirm $ip is being forwarded; PostHog resolves location from the IP property server-side.
Person properties not updatingVerify traits are sent via datafly.identify() and land under properties.$set.
Page views missing from web analyticsConfirm the page event maps to $pageview and carries $current_url.

Notes

  • PostHog’s capture endpoint returns HTTP 200 with {"status": 1} on accept. A bad or missing api_key also returns 200, but with {"status": 0} — Signal treats the latter as a failed delivery, so misconfigured credentials surface in the event debugger rather than passing silently.
  • Self-hosted PostHog is supported: set the ingestion host to your own instance.