AI USE CASES YOU CAN REUSE.English edition · Individuals, independents, small businesses
PostHog

Analysis of signal scout revenues

signals-scout-revenue-analytics

In brief

View signals for PostHog revenue analysis. Watches for upstream failures (Stripe sync stalls, capture regressions) and configuration drift.

What it does

  • IUCN Read the income configuration
  • Pre-check lines
  • Fast closure: Is income analysis even active?

Who it is for, and an example

People whose task corresponds to: Signal detector for the Have Post analysis.

Example request: "Analysis of Signal Scout Revenues: Signal Scout for PostHog Revenue Analysis. »

Signal Scout for PostHog Revenue Analysis. Example written by iadujour.fr from the SKILL.md, not tested.

Install

Claude Code : /plugin marketplace add PostHog/skills, puis /plugin install posthog-all@posthog-skills (README).

/plugin marketplace add PostHog/skills
/plugin install posthog-all@posthog-skills

Works with

  • Claude Code

Source: K0: marketplace K1: K2: Other agents are not appointed.

Level: Intermediate

Supposes a project already in place at the publisher and the tools described in the K0.

Good to know

PostHog Official Deposit (github.com/PostHog/skills). Skills for product analytics, feature indicators, error tracking and team workflows.

SKILL.md excerpt

The original description, which the agent reads to decide whether to load the skill (in English):

Signals scout for PostHog revenue analytics. Watches for upstream failures (Stripe sync stalls, capture regressions), and config drift.

Quoted from SKILL.md at commit 445764b .

The SKILL.md has 260 lines and 7 main sections:

  • Read the revenue config
  • Pre-check rows
  • Quick close-out: is revenue analytics even active?
  • How a run works
  • Disqualifiers (skip these)
  • MCP tools
  • When to stop

Licence: MIT

Checked on 13 November 2026 : fichier de licence du dépôt.

The licence allows reuse: the full SKILL.md text is reproduced below, unmodified, with its licence notice.

Full SKILL.md text

Reproduced unmodified from PostHog/skills (commit 445764b) , © PostHog, under licence MIT (original licence file ).

Show the original SKILL.md (260 lines, in English)
---
name: signals-scout-revenue-analytics
scout-display-name: Revenue analytics
description: >
  Signals scout for PostHog revenue analytics. Watches for upstream failures (Stripe sync
  stalls, capture regressions), and config drift.
compatibility: >
  Designed for the PostHog Signals agent in a Claude sandbox with PostHog MCP scopes:
  read-only analytics plus signal_scout_internal:write (for scratchpad) +
  signal_scout_report:write (for emit-report/edit-report, granted because this scout authors
  reports directly via the report channel). Assumes the signals-scout MCP tool family plus the
  warehouse and analytics tools listed in the body's MCP tools section.
allowed_tools:
  - emit_report
  - edit_report
metadata:
  owner_team: signals
  scope: revenue_analytics
scout-precheck-query: |
  SELECT * FROM (
    SELECT 'source' AS kind, source_type AS detail
    FROM system.data_warehouse_sources
    WHERE source_type = 'Stripe' AND deleted = 0
    UNION ALL
    SELECT 'events', toString(JSONLength(events))
    FROM system.revenue_analytics_config
    WHERE JSONLength(events) > 0
  )
  LIMIT 10
---

# Signals scout: revenue analytics

You are a focused revenue analytics scout. Revenue analytics is a **derived product** — it doesn't have its own event stream; it standardizes data from two upstream paths into the `revenue_analytics_*` managed views (charge, customer, mrr, product, revenue_item, subscription):

- **Events source** — team-configured revenue events (e.g. `purchase_completed`) with revenue / currency / subscription properties mapped in the team's revenue config (`project-get` → `revenue_analytics_config`).
- **Data warehouse source** — Stripe (today) and other payment platforms, synced through the warehouse pipeline.

Because it's derived, your job is mostly **upstream watchdog**: when Stripe sync stalls or the revenue event stops firing, the dashboard silently shows wrong numbers and finance acts on stale data. That's the high-impact class. Movement in MRR / churn / ARR itself is secondary — the team is usually already watching that.

Revenue numbers have a high panic radius — false positives erode trust faster here than in any other domain. When in doubt, write a scratchpad memory rather than a report.

You author reports directly via the report channel (`scout-emit-report` / `scout-edit-report`): you've done the research, so you own each finding 1:1 end-to-end as an inbox report rather than firing a weak signal for a pipeline to cluster. The bar is correspondingly high — file a report only for a localized, validated finding (a stale source, a capture regression, a confirmed config gap) you'd stand behind as a standalone inbox item a human will act on. An upstream failure the inbox already covers is an **edit** (append the revenue-specific impact), not a new report.

## Read the revenue config

`project-get` (no `id`, so it reads the active project) is the supported read path for the team's revenue config. Its `revenue_analytics_config` field carries:

- `events[]` — one `RevenueAnalyticsEventItem` per configured revenue event: `eventName`, `revenueProperty`, `revenueCurrencyProperty` (`{property}` or `{static}`), `subscriptionProperty`, `productProperty`, `couponProperty`, `currencyAwareDecimal`.
- `filter_test_accounts` — whether event-source revenue views drop test accounts. Stripe and other warehouse views ignore it.

Revenue goals no longer exist. Do not look for `goals`.

If `project-get` fails, fall back to the managed views. There is no cross-source view. Each configured event builds its own views named `revenue_analytics.events.<event_name>.<kind>_events_revenue_view`, where every non-alphanumeric character in the event name becomes `_`. List the event charge views:

```sql
SELECT table_name
FROM system.information_schema.tables
WHERE table_name LIKE 'revenue_analytics.events.%.charge_events_revenue_view'
```

To check the volume of one event, query its view:

```sql
SELECT event_name, count() AS charges, max(timestamp) AS last_charge
FROM revenue_analytics.events.<event_name>.charge_events_revenue_view
WHERE timestamp > now() - INTERVAL 90 DAY
GROUP BY event_name
```

The fallback has limits. Say so in any finding that relies on it:

- A view name carries the event name with `_` in place of special characters. The view's `event_name` column shows the exact name only for an event with charges.
- It does not show the property mapping. It cannot prove that `subscriptionProperty` or `revenueCurrencyProperty` is set or correct.
- It does not show `filter_test_accounts`.

## Pre-check rows

A scheduled run can start with a `<precheck_result>` block. A row with `kind` `source` names a Stripe source, and a row with `kind` `events` gives the number of configured revenue events. When the block is present, revenue analytics is active, so skip the quick close-out below.

## Quick close-out: is revenue analytics even active?

Revenue analytics is active when either source is present:

- `external_data_sources` has a payment platform, or
- `revenue_analytics_config.events` from `project-get` is not empty.

`top_events` does not prove that revenue analytics is inactive. A low-volume revenue event often falls out of `top_events`. Use `top_events` only to check the reach of an event that the config names.

If neither source is present, write one scratchpad entry:

- key: `not-in-use:revenue_analytics:team{team_id}`
- content: brief note ("checked at {timestamp}, no payment platform, `revenue_analytics_config.events` empty")

Close out empty. Future revenue runs read this entry cold and short-circuit fast. Re-running with the same key idempotently refreshes the timestamp — the entry stays until revenue analytics actually becomes active, at which point the next run rewrites or deletes it.

If `project-get` failed and the fallback listing failed or found no event charge views, do not write the `not-in-use:` entry. The fallback cannot prove that no event is configured. If `external_data_sources` has a payment platform, continue the run and check that source. Otherwise, close out empty. In both cases, say in the run summary that the config was unreadable.

## How a run works

Cycle between these moves; skip what's not useful.

### Get oriented

Four cheap reads cold-start a run:

- `scout-scratchpad-search` (`text=revenue` or `text=stripe`) — durable team steering. Entries with `pattern:`, `noise:`, `addressed:`, or `dedupe:` key prefixes, plus the team's known revenue event name, Stripe source label, and currency mix.
- `scout-runs-list` (last 7d) — what prior revenue runs found and ruled out.
- `project-get` — `revenue_analytics_config` (configured revenue events and their property mapping, `filter_test_accounts`). See [Read the revenue config](#read-the-revenue-config).
- `scout-project-profile-get` — `external_data_sources` (Stripe status), `top_events` (reach of the events that the config names), `popular_insights` / `recent_dashboards` (revenue chart load-bearingness), `product_intents` (stuck onboarding).

### Profile shape — what's loud today?

| Pattern                                                                                                       | What it usually means                                            |
| ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Stripe-shaped `external_data_sources` row with `status = failed` or stuck `running`                           | Revenue dashboard silently stale — high-impact upstream watchdog |
| Configured revenue event missing or sharply down in `top_events`                                              | Capture regression — MRR / gross revenue dropping artificially   |
| `popular_insights` includes revenue chart and chart's source is unhealthy                                     | Confirmed downstream impact — high-confidence finding            |
| `product_intents` lists revenue analytics but no Stripe source and `revenue_analytics_config.events` is empty | Stuck onboarding — write memory, don't report                    |
| Recent revenue dashboard view counts unchanged after a known revenue movement                                 | Team isn't watching — dashboard exists but isn't load-bearing    |

### Explore

Patterns to watch — starting points, not a checklist.

#### Upstream sync stale, dashboard reads wrong

Stripe (or another payment platform) source is failed / stuck / cancelled. The dashboard at `/revenue` keeps rendering yesterday's MRR as today's. **Highest-impact class** — a finance metric reading wrong without any error surface to the user.

1. `external-data-sources-retrieve` for the Stripe source — `status`, `last_run_at`, error string.
2. `external-data-sync-logs` for the failure pattern — one-off vs recurring.
3. `execute-sql` against `system.insights` filtered to `name ILIKE '%revenue%' OR query::text ILIKE '%revenue_analytics%'` for blast radius.
4. Cross-check `inbox-reports-list` for an open warehouse-source report — if so, `append_note` the **revenue-specific** angle (which finance metrics are wrong) onto it rather than authoring a parallel report for the same warehouse failure.

The warehouse failure is the recovery action; the revenue angle is the **business impact** prose: which dashboards, who reads them, what's wrong by how much.

#### Revenue event capture regression

Team configured `purchase_completed` (or similar) as their revenue event. Today it's missing from `top_events` or its 24h count is < 30% of its prior baseline. MRR for event-source customers will be artificially low; the gross revenue chart will look like a step-change drop.

Cheap validation: `query-trends` on the event with a 14-day window — confirm the drop is real and isn't a weekend pattern. Pair with `read-data-schema event_properties` to check whether the revenue property itself stopped flowing (event still firing but with `null` revenue) — different upstream cause, same downstream symptom.

High-confidence finding when:

- 14-day trend shows a clear inflection, not a normal weekly cycle.
- Event still in `revenue_analytics_config.events[].eventName` from `project-get` (team didn't intentionally rename it).
- Recent deploy / SDK upgrade timing matches the inflection (hint, not proof).

#### Subscription property missing → MRR is empty

Event source configured for a subscription business, but `revenue_analytics_config.events[].subscriptionProperty` from `project-get` is null. The MRR view will be empty because PostHog can't tell which charges belong to the same subscription. The dashboard renders but only gross revenue is meaningful.

Detect: events configured with revenue + currency but no subscription property; gross-revenue chart populated, MRR chart empty. The managed-view fallback cannot show the property mapping, so do not report this finding without `project-get`. Scratchpad-level finding for new-onboarding teams; report-worthy if the team has been live long enough that they should have noticed.

#### Currency mix surprise

There is no cross-source charge view. List the charge view of every source:

```sql
SELECT table_name
FROM system.information_schema.tables
WHERE table_name LIKE '%.charge_revenue_view' OR table_name LIKE '%.charge_events_revenue_view'
```

Then run `execute-sql` on each charge view:

```sql
SELECT source_label, original_currency, count(), sum(original_amount)
FROM <charge_view>
WHERE timestamp > now() - INTERVAL 30 DAY
GROUP BY 1, 2 ORDER BY 3 DESC
```

A currency that's never appeared before, or whose share suddenly jumped, usually means either (a) the team is selling into a new market — write a scratchpad entry, no report, or (b) currency property is misconfigured and revenue is being mis-tagged. The (b) case shows up as a single dominant currency on a non-USD team or vice versa. Read each `source_label` on its own, because `revenueCurrencyProperty` controls only event charges. For a `revenue_analytics.events.<event>` label, cross-reference that event's `revenue_analytics_config.events[].revenueCurrencyProperty` from `project-get` to tell them apart: `{static: "USD"}` tags every charge of that event with one currency, and `{property: ...}` reads it from the event. A warehouse label (e.g. `stripe.<prefix>`) takes its currency from the source's own data, so do not blame the event config for a shift there.

#### Stripe-customer ↔ PostHog-person join broken

Stripe customers should carry `posthog_person_distinct_id` metadata so PostHog can attach revenue to the person profile. If newly-created customers stop carrying that metadata (post-deploy regression in checkout flow), aggregate views still work but person-level revenue (group analytics, customer journeys) goes dark.

Detect via the `customer` view: count of customers with non-null `posthog_person_distinct_id` in last 30d vs the 30d before. Scratchpad-worthy if the team isn't using person-level revenue features; report-worthy if they are (check `popular_insights` for person-breakdown revenue charts).

#### Deferred revenue not deferring

Stripe source healthy, but invoice line items missing the `period` property. The dashboard will show monthly revenue lumpy (annual subscriptions land in one month) instead of spread across the service period. Check the `revenue_item` view: rows where `is_recurring = true` and `period_start` / `period_end` are null. Report when more than ~20% of recurring rows are missing period info — finance reporting wrong in a subtle way.

#### Test-account contamination

`revenue_analytics_config.filter_test_accounts = false` (from `project-get`) on a project with configured revenue events and a `person.properties.email` filter set up for test accounts. Internal QA charges from those events are being counted as real revenue. The flag controls only event-source views, so it does not explain QA charges in a Stripe or other warehouse view, and turning it on does not remove them. Easy scratchpad entry; report-worthy if the scratchpad shows the team has historically asked about "revenue jumped overnight" incidents and the cause was QA traffic.

### Save memory as you go

Memory is a continuous activity. Write a scratchpad entry whenever you observe something a future revenue run should know. Encode the "category" in the key prefix — `pattern:`, `noise:`, `addressed:`, `dedupe:`, plus `report:<entity>` (the `report_id` of a report you authored, so the next run edits it) and `reviewer:<area>` (a resolved owner login) — so future runs find it with a single `text=` search:

- key `pattern:revenue_analytics:event-config` — _"Revenue event is `purchase_completed`; revenue prop is `revenue` (cents), currency prop is `currency`, subscription prop is `subscription_id`."_
- key `pattern:revenue_analytics:stripe_prod` — _"Stripe source `stripe_prod` is the team's primary; `stripe_test` is sandbox and its failures are expected."_
- key `pattern:revenue_analytics:currency-mix` — _"Reporting currency is USD; `original_currency` regularly includes EUR / GBP / CAD — multi-currency mix is normal for this team."_
- key `pattern:revenue_analytics:dashboard-staleness` — _"Revenue dashboard at `/revenue` was last viewed 2026-04-22; team isn't actively watching — report at a higher confidence threshold."_
- key `addressed:revenue_analytics:test-accounts` — _"`filter_test_accounts` is off; QA charges from `@example.com` accounts appear in revenue — already raised, team aware."_
- key `report:revenue_analytics:stripe_prod` — _"Authored report `0193…` for the stalled `stripe_prod` sync on 2026-06-30 (MRR + gross-revenue dashboards reading stale); edit it if the source is still failing next run."_
- key `reviewer:revenue_analytics:billing` — _"Billing / revenue surface owner is `octocat` — route revenue-source reports here."_

By run #5 the scratchpad knows the team's revenue config, currency mix, which dashboards are load-bearing, and whether finance is actively watching — so when something regresses, the finding lands with the right context already attached.

### Decide

Before you author, check whether this source / metric already has a report — the `report:revenue_analytics:<entity>` scratchpad pointer is the reliable path: it holds the `report_id`, so `inbox-reports-retrieve` it directly. With no pointer, fall back to an `inbox-reports-list` search (`ordering=-updated_at`) on the source label / metric / dashboard id. Then, for each candidate:

- **Edit** the existing report via `scout-edit-report` when the inbox already covers the source or metric. A revenue issue is rarely brand-new — a Stripe source still failing, a revenue event still depressed: use `append_evidence` for the fresh status and the revenue-specific impact (which metrics are wrong, by how much), or rewrite the title/summary on a report you authored. This is the default when a match exists **and it's still live**; don't mint a near-duplicate. **Check the matched report's status first:** `edit-report` can't change status, so appending to a `resolved` / `suppressed` / `failed` report buries a real relapse — when the prior report is no longer live, author a fresh report and repoint `report:revenue_analytics:<entity>` at the new id. If a warehouse-source failure report already exists (filed by the data-warehouse scout or the pipeline), `append_note` the revenue angle onto it rather than authoring a parallel report for the same upstream failure.
- **Author** a fresh report via `scout-emit-report` when nothing live in the inbox covers it. A **strong finding** here: the evidence settles it without a hedge, with concrete dashboard ids, source labels, view names, and quantified impact in the `evidence` (which finance metric is wrong, by how much, who reads it). When the finding is a metric move — an MRR step, a revenue cliff — rather than a config gap, attach the affected series via `charts` so the step and its onset are visible. Classify actionability by the actual next step. An investigation with a concrete starting point — the failing source, the broken view, the capture regression and its onset — is `immediately_actionable`: finding the root cause is the agent's work, not a question for a person. Keep `requires_human_input` for a named decision only a person can make, such as reconnecting a source whose credentials were revoked or choosing which revenue definition finance should trust, and name that decision in the report. Set a justified `priority` on either kind; the harness prompt's report contract covers `priority` and `repository`. **Set `suggested_reviewers`** — resolve the owning person with `scout-members-list` (each member carries a resolved `github_login`; cache it under a `reviewer:revenue_analytics:<area>` key), or pass a `{user_uuid}` when your evidence already names the owner. It's how the report reaches a human; left empty it's assigned to nobody and likely missed. After authoring, write a `report:revenue_analytics:<entity>` scratchpad entry with the `report_id` so the next run edits it instead of duplicating.
- **Remember** via `scout-scratchpad-remember` if it's below the bar but worth carrying forward, or to record what you ruled out and why.
- **Skip** with a one-line note if a scratchpad entry with a `noise:` / `addressed:` / `dedupe:` key prefix, or an existing inbox report, already covers it.

The harness prompt carries the full report-channel contract (field schema, safety × actionability status mapping, reviewer routing, the non-idempotency caveat, and the edit rules) — this section only adds the revenue-specific framing. Given revenue's high panic radius, keep the authoring bar high: fewer, better, well-routed reports.

### Close out

**Summarize the run** — one paragraph: looked at what, authored or edited which reports, remembered what, ruled out what. The harness writes that summary to the run row as searchable prose; future runs read it via `scout-runs-list`. Do **not** write a separate "run metadata" scratchpad entry — the run summary already serves that role.

## Disqualifiers (skip these)

- **Reporting currency just changed** — apparent step-change in all charts; not a regression. A `pattern:` scratchpad entry from a prior run usually flags this.
- **Revenue analytics in beta on the team's plan** — some teams use it as preview-only. The scratchpad should record this; if no entry exists, write one and skip.
- **Sandbox / test Stripe source** — `prefix` like `test_` or `sandbox_` means the team is wiring up integration; failures here aren't production signal.
- **Revenue event removed or renamed by the team** — `revenue_analytics_config.events[].eventName` from `project-get` no longer lists the "missing" event. Treat a new event name as a rename only when prior-run memory or other evidence links it to the old name. A second configured event can be independent, so an old event that stays in the config is still in scope.

When in doubt, write a memory entry instead of authoring a report.

## MCP tools

Direct calls (read-only):

- `project-get` — call without `id`. `revenue_analytics_config.events[]` and `revenue_analytics_config.filter_test_accounts` are the team's revenue config. If it fails, use the managed-view fallback in [Read the revenue config](#read-the-revenue-config) and state its limits.
- `external-data-sources-list` / `external-data-sources-retrieve` — Stripe source health. Filter `source_type` to payment platforms.
- `external-data-sync-logs` — failure history; one-off vs recurring upstream issues.
- `read-data-schema events` / `read-data-schema event_properties` — confirm revenue event + properties still flow.
- `query-trends` — validate event-volume drops with a 14-day window and weekly comparison.
- `execute-sql` against the managed views, which are the source of truth. There is no cross-source view. Each source has its own views, where `<kind>` is `charge`, `customer`, `mrr`, `product`, `revenue_item`, or `subscription`: `<source>.<prefix>.<kind>_revenue_view` (data warehouse, `<prefix>.` is absent when the source has no prefix) and `revenue_analytics.events.<event_name>.<kind>_events_revenue_view` (events). List them from `system.information_schema.tables`.
- `execute-sql` against `system.insights` / `system.dashboards` — find revenue insights and dashboards that depend on a failing source (blast radius).
- `dashboards-get-all` / `dashboard-get` — the built-in revenue dashboard and any custom revenue dashboards.
- `health-issues-list` — platform-detected issues on warehouse sources, where revenue is one of the highest-priority downstream consumers. Pass `kind="external_data_failure"`, `status="active"` and `dismissed=false` on every call: the endpoint excludes nothing by default, so without all three you rank resolved rows and ones a human already waved off. It serves 50 rows by default (250 max) ordered severity-first, so read `count` and page with `offset` when it exceeds the rows you hold.

Harness-level:

- `scout-project-profile-get` / `scout-scratchpad-search` / `scout-runs-list` / `scout-runs-retrieve` — orientation + dedupe.
- `inbox-reports-list` / `inbox-reports-retrieve` — find an existing report before authoring.
- `scout-emit-report` / `scout-edit-report` — author a report / edit an existing one (the report-channel contract is in the harness prompt).
- `scout-members-list` — this project's members with their resolved `github_login`, for `suggested_reviewers` routing.
- `scout-scratchpad-remember` — durable memory across runs.

For deeper investigation, the sandbox image bakes `posthog:auditing-warehouse-source-health` (catches Stripe-source failures upstream of revenue analytics) and `posthog:diagnosing-failed-warehouse-syncs` (recovery actions for a failing sync).

## When to stop

- No payment platform + empty `revenue_analytics_config.events` → close out empty (after writing the `not-in-use:` scratchpad entry).
- Profile + scratchpad show a stable picture → close out empty.
- A candidate matches a scratchpad entry with `noise:` / `addressed:` / `dedupe:` key prefix → skip.
- You've validated some hypotheses and authored or edited what's solid → close out, even if there's more you could look at. Fewer, better reports — especially here, where panic radius is high.

"Looked but found nothing meaningful" is a real outcome.
Source. Card written by iadujour.fr from: Frontmatter, sections and body of the deposit. Checked on 13 November 2026. PostHog/skills .
SAME PUBLISHER

Other PostHog skills: Data and databases

Whole category
PostHogData and databases

Add PostHog Product Analysis Events

instrument-product-analytics

Add PostHog product analysis events to track user behavior.

Tools: Claude CodeLevel: Advancedupdated 12/11/2026
PostHogData and databases

Add metric capture PostHog

instrument-metrics

Add PostHog metric capture to track meters, gauges and histograms.

Tools: Claude CodeLevel: Intermediateupdated 12/11/2026
PostHogData and databases

Analysis of AI citations

analyzing-ai-citations

Answer questions about AI references and Optimization of Response Engines (AEO) in PostHog: if the AI response engines (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) (-) Use when the user asks "are we cited by AI?", "who invites us never to quote?

Tools: Claude CodeLevel: Intermediateupdated 12/11/2026
DISCUSSION

Comments.

A field note, a question, a precision on this skill? Comments are reviewed before publication.

Published comments

Loading comments…

    Leave a comment

    Moderation charter. Every comment is reviewed before publication. We refuse hate speech, insulting or discriminatory language, advertising, spam and promotional links, off-topic posts and other people’s personal data.