Skip to content
Back to skills

Posthog Measurements For Goal Bindings

ASecurity

Standardizes creating and debugging PostHog insights for Hamster goal measure bindings. Covers TrendsQuery vs HogQL, the measure binding contract (aggregated_value), math types, formulas, HogQL pitfalls, and the Composio API actions needed. Use when binding a PostHog metric to a goal, debugging a measure showing 0/null, or converting HogQL to TrendsQuery.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
datagoshellnodeexpressdebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add gethamster/skills --skill posthog-measurements-for-goal-bindings --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Posthog Measurements For Goal Bindings?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Posthog Measurements For Goal Bindings
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gethamster-posthog-measurements-for-goal-bindings/badge)](https://www.skillsdirectory.com/skills/gethamster-posthog-measurements-for-goal-bindings)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: posthog-measurements-for-goal-bindings
description: "Standardizes creating and debugging PostHog insights for Hamster goal measure bindings. Covers TrendsQuery vs HogQL, the measure binding contract (aggregated_value), math types, formulas, HogQL pitfalls, and the Composio API actions needed. Use when binding a PostHog metric to a goal, debugging a measure showing 0/null, or converting HogQL to TrendsQuery."
version: "1.0.0"
category: "Workflows"
---

# PostHog Measurements for Goal Bindings

> Reference guide for creating and debugging PostHog insights that bind to Hamster goal measures. Every line here saves 10 minutes of trial-and-error.

## When to Invoke

- Creating a PostHog insight to bind to a Hamster goal measure
- Debugging why a measure binding shows 0 or null
- Converting a HogQL query to a TrendsQuery
- Setting up formula-based metrics (ratios, rates) for goal measures
- User says: "bind this PostHog metric to a goal" or "why is my measure showing 0"

## The Measure Binding Contract (CRITICAL)

Hamster measure bindings read `aggregated_value` from **TrendsQuery** results.

| Query Type | Result Shape | Binding Can Read? |
| --- | --- | --- |
| TrendsQuery | `aggregated_value: <number>` | YES |
| HogQLQuery | `[[value]]` (table format) | NO |

**Rules:**

1. The insight MUST be a `TrendsQuery` -- never `HogQLQuery` for measure bindings.
2. Set `display: "BoldNumber"` for single-value metrics. Other options (`"Number"`, `"TimeSeries"`) may not expose `aggregated_value` correctly.
3. For ratio metrics (A/B), use the `formula` field -- the formula result appears as `aggregated_value`.
4. For non-formula queries, each series returns its own `count` and `aggregated_value`.

## TrendsQuery Structure Reference

```json
{
  "query": {
    "kind": "TrendsQuery",
    "series": [
      {
        "kind": "EventsNode",
        "event": "Chat Message Sent",
        "math": "hogql",
        "math_hogql": "count(DISTINCT properties.thread_id)",
        "properties": [
          {
            "key": "properties.source",
            "value": "ai",
            "operator": "exact",
            "type": "event"
          }
        ]
      }
    ],
    "formula": null,
    "dateRange": { "date_to": null, "date_from": "-7d" },
    "interval": "day",
    "display": "BoldNumber"
  }
}
```

### Fields

- **`series`**: Array of `EventsNode` objects. Each defines an event + math aggregation.
- **`formula`**: String like `"A/B"` where A/B refer to series indices (0-based series, letter-based refs). `null` for single-series.
- **`dateRange`**: `{ "date_from": "-7d", "date_to": null }` for last 7 days. Supports relative (`-30d`, `-1m`) and absolute dates.
- **`interval`**: `"day"`, `"week"`, `"month"`. For BoldNumber, this affects aggregation granularity but the final value is the sum over the range.
- **`display`**: `"BoldNumber"` for single-value metrics (what bindings need). `"TimeSeries"` for trend charts.

### EventsNode Fields

- **`kind`**: Always `"EventsNode"` for event-based series.
- **`event`**: Event name (e.g., `"Chat Message Sent"`).
- **`math`**: Aggregation type (see table below).
- **`math_hogql`**: Required when `math: "hogql"`. Contains the HogQL expression.
- **`math_property`**: Required for `unique_group` (must be a GROUP TYPE, not an event property).
- **`properties`**: Array of property filters. Each: `{ "key": "...", "value": ..., "operator": "exact|gt|lt|...", "type": "event|person|..." }`.

## Math Types Reference

| `math` value | What it does | Extra fields needed |
| --- | --- | --- |
| `"total"` (default) | Total event count | none |
| `"dau"` | Daily active users | none |
| `"weekly_active"` | Weekly active users | none |
| `"monthly_active"` | Monthly active users | none |
| `"unique_session"` | Unique sessions | none |
| `"first_time_for_user"` | First time event for user | none |
| `"first_matching_event_for_user"` | First matching event for user | none |
| `"unique_group"` | Unique group count | `math_property` = GROUP TYPE (e.g., `"account"`, `"project"`) |
| `"hogql"` | Custom HogQL aggregation | `math_hogql` = HogQL expression string |

### Critical Gotchas

1. **There is NO `"unique"` math type.** To count unique values of an event property (e.g., distinct `thread_id`), use `math: "hogql"` with `math_hogql: "count(DISTINCT properties.thread_id)"`.

2. **`unique_group` is for GROUP ANALYTICS, not event properties.** `math: "unique_group"` with `math_property: "thread_id"` does NOT count unique thread IDs -- it silently falls back to `count()`. `math_property` must be a group type name (like `"account"`), not an event property name.

3. **Use `math_hogql`, NOT `math_property`, for custom HogQL expressions.** `math_property` is only for `unique_group`. If you put a HogQL expression in `math_property`, it will be ignored.

## Step-by-Step: Creating a Measure-Binding Insight

1. **Determine the metric formula.** What are you counting? Is it a ratio (A/B)? A single count?
2. **Choose the right math types.** Map your intent to the math types table above. When in doubt, use `math: "hogql"` with an explicit expression.
3. **Build the TrendsQuery JSON.** Use the structure reference above. Set `display: "BoldNumber"`.
4. **Create the insight** via `POSTHOG_CREATE_CUSTOM_PROJECT_INSIGHTS` with the query object.
5. **Fetch the result** via `POSTHOG_CREATE_QUERY_IN_PROJECT_BY_ID` with `refresh: true` to force computation.
6. **Verify `aggregated_value`** is non-null and matches expectations. If null or 0, run the debugging checklist.
7. **Update the goal measure binding** to point to the new insight ID.

## HogQL Pitfalls

These are the silent failures that cost hours:

| Pitfall | Symptom | Fix |
| --- | --- | --- |
| `properties.$session_id` | Returns NULL silently | Use `properties['$session_id']` (bracket syntax for `$`-prefixed keys) |
| `properties.thread_id` | Works fine | Dot syntax is OK for non-`$` properties |
| `'none'` sentinel | Missing values appear as literal string `'none'` | Treat as absent; filter with `IS NOT NULL AND != 'none'` |
| Shell escaping | Complex HogQL breaks when passed via CLI | Write to a temp file, or use JSON body directly via Composio |
| `OPERATION_PROPERTY_NONE` | Literal string `'none'` in results | Same as above -- it is the missing-value sentinel |

## Common Patterns

### Unique count of a property

```json
{ "kind": "EventsNode", "event": "Chat Message Sent", "math": "hogql", "math_hogql": "count(DISTINCT properties.thread_id)" }
```

### Unique sessions

```json
{ "kind": "EventsNode", "event": "Chat Message Sent", "math": "unique_session" }
```

### Total events (default)

```json
{ "kind": "EventsNode", "event": "Chat Message Sent", "math": "total" }
```

### Ratio metric (e.g., unique threads / total sessions)

```json
{
  "query": {
    "kind": "TrendsQuery",
    "series": [
      { "kind": "EventsNode", "event": "Chat Message Sent", "math": "hogql", "math_hogql": "count(DISTINCT properties.thread_id)" },
      { "kind": "EventsNode", "event": "Chat Message Sent", "math": "unique_session" }
    ],
    "formula": "A/B",
    "dateRange": { "date_from": "-7d", "date_to": null },
    "interval": "day",
    "display": "BoldNumber"
  }
}
```

### Filtered series (only AI-sourced messages)

```json
{
  "kind": "EventsNode",
  "event": "Chat Message Sent",
  "math": "total",
  "properties": [
    { "key": "properties.source", "value": "ai", "operator": "exact", "type": "event" }
  ]
}
```

## Anti-Patterns

| Anti-pattern | Why it fails |
| --- | --- |
| Using `HogQLQuery` instead of `TrendsQuery` | Returns `[[value]]` table format -- binding cannot read `aggregated_value` |
| Using `unique_group` with an event property name | Silently falls back to `count()` -- `math_property` must be a group TYPE |
| Using `math_property` instead of `math_hogql` for custom HogQL | `math_property` is ignored for `math: "hogql"` -- use `math_hogql` |
| Forgetting `refresh: true` when fetching results | Returns stale or null cached results |
| Using `properties.$session_id` in HogQL | Returns NULL silently -- use `properties['$session_id']` |
| Using `display: "TimeSeries"` for a binding metric | May not expose `aggregated_value` -- use `"BoldNumber"` |

## Debugging Checklist

When a measure binding shows 0, null, or wrong value, check in order:

1. **Is the insight a TrendsQuery?** (not HogQLQuery) -- HogQL returns tables, not `aggregated_value`.
2. **Does the result contain `aggregated_value`?** -- Fetch via `POSTHOG_CREATE_QUERY_IN_PROJECT_BY_ID` and inspect the response.
3. **Is `display` set to `"BoldNumber"`?** -- Other display types may not expose the value correctly.
4. **Are you using `math_hogql` (not `math_property`) for custom HogQL?** -- Check the series object.
5. **Are `$`-prefixed properties accessed with bracket syntax?** -- `properties['$session_id']` not `properties.$session_id`.
6. **Did you fetch with `refresh: true`?** -- Without it, you may get stale/null cached results.
7. **Is the date range correct?** -- Check `dateRange.date_from` and `dateRange.date_to`. A too-narrow range returns 0.
8. **Are there events matching the filter?** -- Verify the event exists and properties match in the PostHog UI.
9. **For formulas: does the denominator ever equal 0?** -- Division by zero returns null, not an error.

## PostHog Composio Actions

Key actions for measure binding work:

| Action | Purpose |
| --- | --- |
| `POSTHOG_CREATE_CUSTOM_PROJECT_INSIGHTS` | Create a new insight with a custom TrendsQuery |
| `POSTHOG_CREATE_QUERY_IN_PROJECT_BY_ID` | Execute a query and get results (use `refresh: true`) |
| `POSTHOG_LIST_ORGANIZATION_PROJECTS` | List projects to get project IDs |
| `POSTHOG_LIST_PROJECT_GROUP_TYPES` | List group types (for `unique_group` math) |

For the full set of 200+ PostHog actions, call `list_app_actions` with `app: "posthog"`.

### PostHog Project IDs

- **prod**: `371628`
- **tux**: `371881`
- API host: `https://us.posthog.com`

## Cross-References

- **`hamster-analytics`** -- Event instrumentation patterns (how to track events in code)
- **`hamster-instrumentation`** -- Plan-time observability checklist (what to track before building)
- **`hamster-posthog-dashboards`** -- Dashboard analysis and reporting workflows
- **`hamster-posthog-user-audit`** -- Per-user event auditing (Q1-Q8 playbook)
- Hamster docs: [Metrics](https://tryhamster.com/docs/hamster-studio/metrics) | [Goals](https://tryhamster.com/docs/hamster-studio/goals)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…