A collection of PostHog skills for enhancing AI-assisted workflows. Add this repo as a Claude Code plugin marketplace to get access to all PostHog skills: Then install individual plugins: Or browse available plugins: Copy any skill directory to .claude/skills/ in your project: Any directory under skills/ that contains a .claude-plugin/plugin.json is automatically discovered and added to the market
Add this skill
npx mdskills install PostHog/creating-ai-subscription@PostHog? Sign in with GitHub to claim this listing.Comprehensive guide for creating AI-generated recurring reports with clear gates, examples, and edge cases
1---2name: creating-ai-subscription3description: >4 Create a recurring AI-generated PostHog report — schedule a free-text prompt to5 run on a cron, with the LLM-synthesized markdown delivered to email or Slack on6 each tick. Use when the user wants a recurring AI summary of X on any cadence7 (daily, weekly, monthly, yearly) rather than a one-off report. (To attach an AI8 summary to an existing insight/dashboard9 subscription instead of a free-text prompt, see `managing-subscriptions` and its10 `summary_enabled` option.)11---1213# Creating a prompt subscription1415## When to use this1617A **subscription** delivers a PostHog report to email or Slack on a recurring18schedule. There are three kinds, distinguished by which field you set — the kind is19derived and returned as the read-only `resource_type`:2021- **`insight`** — periodic snapshots of one existing insight (`resource_type: "insight"`)22- **`dashboard`** — periodic snapshots of a dashboard's tiles (`resource_type: "dashboard"`)23- **`prompt`** — a recurring **AI-generated** report from a free-text prompt: an LLM24 plans and runs HogQL over the project's data and synthesizes a fresh markdown report25 each tick (`resource_type: "ai_prompt"`)2627Use **this** skill for the **prompt** kind — i.e. when the user wants a recurring AI28summary of X (on any cadence — daily, weekly, monthly, yearly) rather than a recurring29snapshot of one existing insight/dashboard, or a single one-off report. Pick a prompt subscription when the30value is the _analysis itself_ (the LLM deciding what to query and writing it up),31not a fixed chart they already built. For an insight/dashboard subscription, set32`insight`/`dashboard` instead of `prompt` and the AI gates below don't apply.3334This skill covers **creating** the subscription. Once it exists you manage its35lifecycle with the same `subscriptions-*` tools (see below): list it, edit/disable/36re-enable it, send a test delivery, or delete it.3738## Tools3940| Tool | Purpose |41| -------------------------------------------- | --------------------------------------------------- |42| `posthog:subscriptions-create` | Create the recurring prompt subscription |43| `posthog:subscriptions-list` | Confirm it landed; inspect existing subscriptions |44| `posthog:subscriptions-partial-update` | Edit, disable (`enabled: false`), or re-enable it |45| `posthog:subscriptions-test-delivery-create` | Send an immediate test delivery to its target(s) |46| `posthog:subscriptions-delete` | Soft-delete it (stops all future deliveries) |47| `posthog:integrations-list` | Find a Slack `integration_id` (filter `kind=slack`) |48| `posthog:integrations-channels-retrieve` | List a Slack integration's channels (id + name) |4950## What you need before calling5152The endpoint enforces three create-time gates and will return 400 if any fails:53541. **PostHog Cloud, or `DEBUG=true`** — self-hosted production deployments are not55 eligible (the LLM call routes through a PostHog-managed key).562. **Org-level "AI data processing approved"** — must be toggled on in57 `Org settings → Data → AI data processing`. The user must opt in to AI features58 for the organization first.593. **Prompt subscriptions enabled** for the organization — a PostHog-managed rollout60 flag. If it's off, the org has not been granted access yet; tell the user to61 reach out to PostHog to enable it (there is no self-serve toggle).6263If any of the three is missing, stop and tell the user which one to fix —64re-calling the tool will not help.6566Your access token also needs the **`query:read`** scope in addition to67`subscription:write`: a prompt subscription runs LLM-generated HogQL over the project's68data, so the backend requires query access to create, edit/re-enable, test-deliver,69or delete one. A `subscription:write`-only token is rejected with a 403.7071## Required arguments7273```yaml74prompt: "..." # ≤4000 chars; setting this (with no insight/dashboard) makes it a prompt sub → resource_type "ai_prompt"75target_type: "email" | "slack" # webhook is rejected for prompt subs76target_value: "..." # comma-separated emails, or "<channel_id>|<channel_name>"77frequency: "daily" | "weekly" | "monthly" | "yearly"78interval: 1 # 1 = every tick; 2 = every other tick; etc.79start_date: "2026-09-15T09:00:00Z" # anchors the recurrence + time-of-day; need not be in the future — the scheduler delivers the next occurrence80title: "..." # display name in the subscriptions list81```8283There is no `resource_type` argument to send — the kind is **derived**84from which field you set (`prompt` ⇒ AI report) and returned as the read-only `resource_type`.8586## Optional arguments8788```yaml89byweekday: ['monday', 'wednesday'] # weekly only — days the rrule fires90bysetpos: 1 # most useful with monthly; requires byweekday — e.g. byweekday:['monday']+bysetpos:-1 = last Monday91count: 10 # cap total deliveries92until_date: '2026-12-31T00:00:00Z' # stop on/before this date93integration_id: 42 # Slack only — required; from integrations-list (see "Slack target")94```9596## Slack target9798`target_value` must be `<channel_id>|<channel_name>` (the format the integration99returns). Build it in three steps:1001011. `posthog:integrations-list` filtered by `kind=slack` → pick the Slack102 integration's `id`.1032. `posthog:integrations-channels-retrieve` with that `id` → pick a channel; it104 returns each channel's `id` and `name`, which you assemble into `target_value`105 as `<id>|<name>`.1063. Pass that integration's `id` as `integration_id` — the subscription is pinned107 to one specific Slack integration so reconnections elsewhere don't accidentally108 re-route deliveries.109110## Examples111112### Weekly Monday-morning AI summary by email113114```yaml115prompt: 'Top events week over week, with the biggest drops and any new failure modes called out.'116target_type: email117target_value: founders@acme.example118frequency: weekly119interval: 1120byweekday: ['monday']121start_date: '2026-09-14T08:00:00Z'122title: 'Weekly product pulse'123```124125### Daily Slack report at 9am126127```yaml128prompt: "Yesterday's sign-ups, where they came from, and any errors they hit during onboarding."129target_type: slack130target_value: 'C0123456789|growth-updates' # <channel_id>|<channel_name>; only the channel id is used, the name is cosmetic131integration_id: 42132frequency: daily133interval: 1134start_date: '2026-09-15T09:00:00Z'135title: 'Daily onboarding watch'136```137138## Pitfalls139140- **The kind is immutable.** It's derived from which relation is set, so you can't flip an141 insight or dashboard sub into a prompt sub after the fact (or vice versa) — a PATCH that adds a142 `prompt` to an insight sub is rejected. Pick the right kind at create time.143- **Re-enabling a previously auto-disabled prompt sub** has two preconditions, both144 enforced on the PATCH: (1) a valid `prompt` — already persisted on the row, or a145 new one in the PATCH body (so bare `{"enabled": true}` works when the stored prompt146 is still valid, but is rejected when the disable cause was an invalid prompt until147 you supply a good one); and (2) the **original creator is still an active user** —148 if that account was deactivated the sub cannot be re-enabled at all (no prompt will149 help; re-create it instead).150- **`next_delivery_date` is server-computed from the rrule.** Don't try to set it151 manually — it's read-only. The first delivery fires at the first `start_date`152 occurrence that is at least a short buffer (currently ~15 minutes) in the future,153 so a `start_date` only seconds ahead rolls to the next occurrence.154- **Transient send failures retry; only permanent failures auto-disable.** A155 transient failure (Slack rate limit, SMTP blip, network) fails that delivery and156 is retried by Temporal within the run, then re-fires on the next scheduled tick —157 it does **not** auto-disable the subscription, so a persistently-failing channel158 will keep retrying every tick until you fix it. Only permanent/structural causes159 auto-disable: a disconnected Slack integration, a revoked channel permission, an160 invalid prompt, or revoked AI data-processing consent. (For multi-recipient email,161 a delivery only fails when _every_ recipient fails; partial successes still send.)162 Within a single delivery run the rendered markdown is cached, so Temporal retries163 of that run don't re-run the LLM pipeline — but each new scheduled tick generates a164 fresh report.165166## After it lands167168`subscriptions-list` will return the new row. Confirm `resource_type: "ai_prompt"`,169`enabled: true`, `next_delivery_date` is in the future, and `prompt` matches what170you sent. The first scheduled tick will run the planner → HogQL → synthesis171pipeline and email/Slack the rendered markdown.172
Full transparency — inspect the skill content before installing.