How PostHog actually processes an event you send
At a high level: an event is sent (via SDK or direct API call) with a project identifier, is queued for ingestion, gets processed against any configured transformations or person-property updates, and is persisted for query. Knowing this pipeline exists matters practically for one reason: there is a delay between sending and queryability, and a malformed event can fail silently at more than one stage depending on what exactly is wrong with it.
API keys: project versus personal, and why the distinction matters
- Project API key - used by SDKs and the capture API to send events into a specific project. This is what belongs in client-side code (it is designed to be public-facing) and what identifies which project an event lands in.
- Personal API key - used for authenticated access to PostHog's management and query APIs (reading data, managing flags, admin actions). This should never end up in client-side code - it carries account-level permissions, not just "send an event to this project" scope.
Confusing the two is a real, recurring mistake: a personal API key exposed in a frontend bundle is a credential leak, not a minor misconfiguration.
Sending events: SDK versus raw API
| Approach | When it's the right call |
|---|---|
JavaScript SDK capture() | Standard client-side tracking - handles session/person context, batching, and retry behavior for you |
| Server-side SDKs | Events that should not depend on the client (billing events, backend-triggered actions) or where you need guaranteed delivery independent of the browser |
| Direct capture API call | Environments without an official SDK, or custom pipelines where you are already managing batching/retry yourself |
Schema management: naming discipline before volume, not after
"How Posthog works" as a pipeline is the mechanical half of this; the other half is the same discipline covered in the autocapture-vs-explicit-events guide - a documented naming convention for events and properties, enforced before volume makes cleanup expensive. Schema management tooling helps enforce this once it exists, but it does not create the convention for you.
How to verify it worked
- Send a test event and confirm it appears in PostHog's live events view within the expected ingestion delay - if it never appears, check the API key type and project match before assuming a code bug.
- Confirm the event carries the properties you expect, correctly typed - a string sent where PostHog expects a number (or vice versa) can silently break downstream filtering even though the event itself ingested successfully.
- For server-side sends, confirm delivery does not silently fail on network errors - check whatever retry/error-handling the SDK or your own integration provides, rather than assuming "sent" means "arrived."
- Audit that no personal API key appears anywhere in client-side/browser-loaded code - a quick search of your shipped JavaScript bundle for the key prefix is a five-minute check worth doing once.