Definitions / Infrastructure as code

Definitions lets you manage PostHog dashboards, insights, feature flags, actions, cohorts, endpoints, event definitions, property groups, experiments (with holdouts and saved metrics), and project settings as TypeScript files in your repo. Commit them alongside your application code and run npx posthog-definitions apply to sync them to your project.

When this could help

The UI is a fine default. A code-first workflow can help when:

  • You want a PR review trail for dashboard and flag changes.
  • You manage more than one project – e.g. keeping feature flags in sync between a dev project and production so the same keys, variants, and rollout conditions exist in both.
  • You want an undo button. git revert + apply rolls a change back.
  • Your analytics already live in your repo and you'd like the dashboards and flags next to them.
Definitions is in alpha

The SDK surface, CLI flags, and on-disk file format may change between releases. Found a bug or have an idea? Open a GitHub issue — we'd love your feedback.

Install

Terminal
npm install --save-dev @posthog/definitions

The package ships a posthog-definitions CLI that runs via npx.

Authenticate

Create a personal API key (EU: eu.posthog.com) with read/write scopes for every resource you define. The full set: dashboard, insight, feature_flag, action, endpoint, event_definition (also covers property groups), experiment (also covers experiment holdouts), and experiment_saved_metric.

Then set the key and your numeric project ID in a .env (or .envrc) file:

Terminal
POSTHOG_PERSONAL_API_KEY=phx_...
POSTHOG_PROJECT_ID=000000
# POSTHOG_HOST=https://eu.posthog.com # optional, defaults to us.posthog.com

Define a dashboard

Create posthog/dashboards/growth.ts:

TypeScript
import { dashboard, insight, trends, text } from "@posthog/definitions"
const signups = insight({
key: "weekly-signups",
name: "Weekly signups",
query: trends({
series: [{ event: "user signed up", math: "total" }],
interval: "week",
dateRange: { date_from: "-90d" },
}),
})
export default dashboard({
key: "growth",
name: "Growth",
description: "Top-of-funnel and activation",
pinned: true,
tags: ["growth"],
tiles: [
{ insight: signups, layout: { x: 0, y: 0, w: 6, h: 4 } },
text({ body: "Updated weekly.", layout: { x: 0, y: 4, w: 12, h: 1 } }),
],
})

Each file under posthog/ must export default a single resource. The key is the stable identity – the CLI uses it to match local files to server resources across runs. Resources go in folders by type: posthog/dashboards/, posthog/insights/, posthog/feature-flags/, posthog/actions/, posthog/cohorts/, posthog/endpoints/, posthog/events/, posthog/experiments/, and posthog/project-settings/.

Apply

Preview what would change without making API calls:

Terminal
npx posthog-definitions apply --dry-run

Sync to PostHog:

Terminal
npx posthog-definitions apply

apply is idempotent – re-running with no changes is a no-op.

The CLI only touches resources it created (tagged iac:<resource>:<key>, e.g. iac:dashboards:growth or iac:feature-flags:new-checkout). Resources you built in the UI are invisible to apply: not modified, not deleted, not warned about. You can adopt Definitions one resource at a time without risk to the rest of the project.

By default apply never deletes anything. Pass --prune to delete IaC-tagged resources whose source files have been removed.

Pull

Bootstrap a definitions directory from an existing project:

Terminal
npx posthog-definitions pull

This writes a TypeScript file per resource to the matching folder under posthog/. Review with git diff before committing – existing files with the same name are overwritten.

What you can define

ResourceSDK
Dashboardsdashboard() (with text(), button())
Insightsinsight() with trends() or hogql()
Feature flagsfeatureFlag()
Actionsaction()
Cohortscohort()
Endpointsendpoint()
Event definitionseventDefinition()
Schema property groupspropertyGroup()
Experimentsexperiment()
Experiment holdoutsexperimentHoldout()
Experiment saved metricsexperimentSavedMetric()
Project settingsprojectSettings()

funnel(), retention(), paths(), stickiness(), and lifecycle() query constructors are planned post-MVP.

Type-safe capture()

Event definitions and property groups feed createTypedPostHog, which wraps any posthog-js-shaped client and type-checks posthog.capture(name, properties) at compile time against the same specs synced via apply. Rename or remove an event in your definitions and every callsite becomes a TypeScript error.

Community questions

Was this page useful?

Questions about this page? or post a community question.