What this does, and what it deliberately does not
Evidence in Pathmode is something a person can decide from. A survey response and an error-tracking issue qualify: each one says something a user hit. A raw metric usually does not, because a number with no interpretation attached is the pile the decision loop exists to reduce.
So this guide wires up the signals that carry meaning and leaves the dashboards where they are. If you want a chart in Pathmode, the honest answer is to look at it in PostHog.
Setup
Create an ingest key in Pathmode
Open Settings → API keys, name the key after the tool that will hold it, and set the access dropdown to Evidence ingest only. Copy it once. It is not retrievable afterwards.
An ingest key posts evidence and can do nothing else. It cannot read your intents, change a status, mint a share link, or touch verification. That matters here specifically, because the key is about to live in someone else's system.
PostHog stores destination headers as ordinary configuration, not as a hidden secret, so anyone with access to your PostHog project's destinations can read the key you paste in. That is a good reason to use an ingest-scoped key rather than a full-access one, and to revoke it in Pathmode rather than only deleting the destination.
Find your product id
Open the product in Pathmode. The id is the UUID in the URL. Every ingested item is filed against one product, and a request naming a product outside your workspace is refused with a 404.
Create the PostHog destination
In PostHog, go to Data pipelines, click + New → Destination, search for Webhook, and click + Create. Then fill in five fields.
| Field | Value |
|---|---|
| Webhook URL | https://pathmode.io/api/v1/evidence/ingest |
| Method | POST, which is the default. |
| Headers | Authorization with the value Bearer pm_live_…, keeping the default Content-Type: application/json. |
| JSON Body | One of the payloads below. PostHog templates values with single curly braces, so {event.uuid} and {event.properties.$foo} are substituted before the request is sent. |
| Filters | The event matcher and any property conditions. Not optional decoration for either example below, and the surveys section explains why. |
Then click Create & Enable. Turning on Log responseswhile you set this up makes Pathmode's replies visible in PostHog, which is the fastest way to see a 400 explaining itself.
Test before trusting it
In the destination's Testing section, click Start testing → Test function to send a sample event. Or check the Pathmode side on its own first:
curl -X POST https://pathmode.io/api/v1/evidence/ingest \
-H "Authorization: Bearer pm_live_YOUR_INGEST_KEY" \
-H "Content-Type: application/json" \
-d '{
"productId": "YOUR_PRODUCT_ID",
"source": "posthog",
"type": "observation",
"externalId": "setup-check-1",
"content": "Checking the Pathmode evidence webhook"
}'A first delivery returns 201:
{
"created": 1,
"duplicates": 0,
"items": [
{
"id": "d4e5f6a7-b8c9-4012-a345-6789bcdef012",
"type": "observation",
"content": "Checking the Pathmode evidence webhook",
"source": "posthog",
"severity": null,
"triageStatus": "inbox",
"createdAt": "2026-08-25T12:00:00.000Z"
}
]
}Send the exact same request again and nothing is stored a second time:
{
"created": 0,
"duplicates": 1,
"items": []
}Both items land in the product's evidence inbox. If your Pathmode instance sits behind a firewall that filters by origin, PostHog publishes the egress IPs its pipeline sends from and they need to be allowed.
Survey responses
Set the event matcher to survey sent and add a property filter for $survey_completed = true.
That filter is the whole trick. When partial responses are enabled, PostHog emits a survey sentevent every time the respondent answers one more question, and each event repeats every answer so far. Without the filter, a five-question survey arrives as five separate deliveries, each a superset of the last, and you get five evidence items for one person's opinion. Filtering to completed submissions gives you exactly one.
Deduplicating on {event.uuid} does not help, because each partial is a genuinely distinct event.
Use {event.properties.$survey_submission_id} as the externalIdrather than the event uuid. Every event in one respondent's attempt shares that submission id, so it stays stable across a redelivery and across the partial events if you ever loosen the filter.
Response keys are per question. In PostHog, open the survey's results and use Copy response key on the question card to get the exact $survey_response_… property, then paste it in place of the placeholder:
{
"productId": "YOUR_PRODUCT_ID",
"source": "posthog",
"type": "quote",
"externalId": "{event.properties.$survey_submission_id}",
"sourceUrl": "{event.url}",
"content": "Survey response: {event.properties.$survey_response_YOUR_QUESTION_ID}"
}type is one of friction, quote, observation, metric, or request. A verbatim answer is a quote, which is the type that reads best when it is later cited under a decision.
Error-tracking issues
Send issues, not exceptions. PostHog's error tracking captures a $exception event per occurrence, so a destination matched to that event turns one broken code path into thousands of evidence items. Issue-level alerting fires once when an issue is created or reopened, which is the granularity a product decision is actually made at.
Go to error tracking's configuration page, open Alerting, click New notification, and point it at an HTTP Webhook destination configured as above. You can filter on issue properties, which is useful if auto-assignment already routes issues to a team.
The payload variables available to an issue alert are not the same set as the event globals used elsewhere on this page. Copy the exact keys from the variable list PostHog shows on the alert's own configuration screen rather than assuming them, and use the Testing section to confirm the body renders before you rely on it.
If you do choose a real-time $exception destination anyway, add an event matcher for it, filter hard on the properties you care about, and keep the event uuid as the external id so at least retries do not compound the volume:
{
"productId": "YOUR_PRODUCT_ID",
"source": "posthog-errors",
"type": "friction",
"severity": "high",
"externalId": "{event.uuid}",
"sourceUrl": "{event.url}",
"content": "Error tracking issue: {event.event}"
}Note the different source value. Deduplication is scoped per source, so keeping surveys and errors on separate source names means an id collision between two PostHog features can never hide one behind the other.
Why externalId matters
Webhook deliveries retry. Without an externalId every retry writes another evidence row, and your inbox slowly fills with the same signal wearing different ids.
Supply one and Pathmode stores it once per workspace and source. A repeat delivery returns 200 with created: 0, which is a success on purpose: a 4xx would make a well-behaved sender keep retrying forever. Skipped duplicates cost nothing against your evidence allowance.
The field is optional, so senders that predate it keep working unchanged. Anything new should set it.
Rotating and revoking
Ingest keys are revoked individually. In Pathmode, open Settings → API keys, create the replacement first, paste it into the PostHog destination's Authorization header, confirm a test delivery still returns 201, and only then revoke the old key. Revoking first means dropped deliveries in the window between.
Deleting the PostHog destination stops the sending but does not invalidate the key. If a key may have been seen by someone who should not have it, revoke it in Pathmode.
The full request contract, including batch delivery of up to 50 items and every error code, is in the developer docs. Other integrations are listed on the integrations page.