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.

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.