Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Work safely with endpoint versions — preview a draft in the playground, roll back to an older version, update settings on one version without bumping query history, deactivate a specific version. Use when the user asks \"how do I roll back my endpoint\", \"preview my changes before publishing\", \"I want to fix v5 without bumping the version\", or anything involving the version history. Calls out
.claude/skills/kunanonj-cursor-plugin-posthog-managing-endpoint-versions/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 155% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 22% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 56% | 0% |
This skill is the practical guide to endpoint versioning. It covers the today-workflow, which has some sharp edges worth being explicit about.
data_freshness_seconds on a specific version?"| Behaviour | Reality | | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | | Query change | Auto-cuts a new version. Saving any edit to the query creates a new version and bumps the current version number | | Settings change (description, data_freshness_seconds, materialisation) | Does not cut a new version. Updates the targeted version in place | | The "current" version | Always the highest version number — it's not a pointer you can move backwards | | Calling without ?version=N | Runs the latest version. So unpinned callers always hit the newest | | Disabling the whole endpoint | endpoint-update with is_active: false (no version) takes every version offline at once | | Disabling a single version | endpoint-update with version + is_active: false retires one version without affecting the others |
The model is forward-only. There is no "make v3 the default again" operation today. Practically this means "rollback" requires either creating a new top version that re-uses the old query, or pinning callers to ?version=N.
| Tool | Purpose | | ------------------- | ----------------------------------------------------------------------------------------------- | | endpoint-versions | List all versions for an endpoint, latest first | | endpoint-get | Full config; supports ?version=N to fetch a specific version | | endpoint-update | The workhorse — supports version body param to target a specific version | | endpoint-run | Execute a version directly via ?version=N (without affecting which version other callers hit) |
There is no "draft" concept in the model. Editing the query commits it as a new version immediately. To preview safely:
execute-sql tool (or the SQL editor) — not on the live endpointendpoint-run with ?version=N to confirm the new version returns what you expect"soft launch"
If the user needs a true staging endpoint, the only workaround today is a sibling endpoint with a _v2 or _staging suffix. Document this honestly — there is no in-product staging path.
The forward-only model means "rollback" requires forking:
endpoint-versions to find the version with the good query (say v3)endpoint-get with ?version=3 to retrieve that version's query JSONendpoint-update with the v3 query as the new query — this creates a new version (e.g.v6) with the same query as v3
?version=N now hit v6 (== v3's query)The old version (v5, the broken one) still exists and is still callable via ?version=5 until explicitly deactivated.
Faster mitigation if you can change every caller: have them pin to ?version=3 until a real fix is ready. Lower-impact than cutting a new version.
endpoint-update accepts a version field in the body. When set, settings updates apply to that version only — they do not cut a new version. Useful when:
data_freshness_seconds on an old version that some callers still pin toImportant: passing query together with version is rejected — query changes always cut a new top version, never modify history. The version arg only affects settings.
To take v3 out of service while keeping v4 and v5 callable:
textendpoint-update {name: "...", version: 3, is_active: false}
This sets is_active: false on v3 only. Callers pinned to ?version=3 start getting an error; other callers are unaffected.
To re-enable: same call with is_active: true.
The whole-endpoint is_active field (without version) is a separate switch — it disables every version at once. Use the version-scoped form for surgical takedowns.
Old versions accumulate over time. To find which are dead, call endpoint-versions and read each version's last_executed_at: a version that's null or long stale hasn't been called recently. Materialised dead versions are the costly ones — disable their materialisation with endpoint-update + version + is_materialized: false, and deactivate with is_active: false to signal they're retired.
Confirm with the user before retiring a version: last_executed_at counts only personal-API-key calls and is recorded only for runs since that tracking was added (so a used version can still read null), and a caller may be pinned to ?version=N. The full audit flow lives in auditing-endpoints.
textUser: "I shipped a broken query last night, v5. How do I roll back?" Agent: - endpoint-versions <name> → v5 (latest), v4, v3, v2, v1 - endpoint-get <name> ?version=4 → query JSON for v4 - "Rolling back means creating v6 with v4's query. v5 stays as a historical version but nobody hits it unless they explicitly pass ?version=5. Sound right?" - User confirms - endpoint-update <name> {query: <v4 query>} → creates v6 - endpoint-run <name> ?version=6 to confirm shape - "Done. v6 is live with v4's query. Want me to also deactivate v5 so it's clear it's defunct?" - User: "Yes" - endpoint-update <name> {version: 5, is_active: false}
always going up. If the user is uncomfortable with the resulting history noise, that's a fair concern — surface it honestly.
If the user wants to fix a typo in v5's description without bumping to v6, use the version param.
by default — that's always the highest version number.
{endpoint_name}_v{version}. Disabling materialisation on one version doesn't affect others.
?version=N areinsulated from query edits; unpinned callers always hit the latest and can be surprised by a new version. Encourage consumers to pin, validate a new version, then bump the pin deliberately.
posthog-cli exp endpoints {pull,push,diff} lets the userkeep endpoint definitions as YAML in version control and review changes before pushing — a cleaner workflow than editing live when query changes need review.
flip a pointer rather than fork — surface it as a feature gap (and nudge the team via agent-feedback). Don't pretend endpoint-update does it.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | fail→fail | 12,484 | 12,606 | +1% | 1 | 1 | 0% | 2,053 | 3,310 | +61% | 0 | 0 | — |
case-01 | fail→fail | 9,396 | 5,526 | -41% | 1 | 1 | 0% | 1,652 | 2,348 | +42% | 0 | 0 | — |
case-02 | fail→fail | 6,299 | 5,427 | -14% | 1 | 1 | 0% | 966 | 2,582 | +167% | 0 | 0 | — |
case-03 | fail→fail | 13,973 | 7,660 | -45% | 1 | 1 | 0% | 2,549 | 3,173 | +24% | 0 | 0 | — |
case-04 | fail→pass | 11,220 | 5,484 | -51% | 1 | 1 | 0% | 1,791 | 2,882 | +61% | 0 | 0 | — |
case-06 | pass→pass | 9,276 | 7,187 | -23% | 1 | 1 | 0% | 1,558 | 3,168 | +103% | 0 | 0 | — |
case-07 | fail→pass | 11,200 | 8,498 | -24% | 1 | 1 | 0% | 1,701 | 2,544 | +50% | 0 | 0 | — |
case-08 | pass→pass | 9,076 | 3,644 | -60% | 1 | 1 | 0% | 1,569 | 2,571 | +64% | 0 | 0 | — |
case-09 | fail→pass | 5,780 | 1,939 | -66% | 1 | 1 | 0% | 884 | 2,258 | +155% | 0 | 0 | — |
case-10 | fail→pass | 12,970 | 3,623 | -72% | 1 | 1 | 0% | 2,011 | 2,462 | +22% | 0 | 0 | — |
case-11 | fail→pass | 9,276 | 2,638 | -72% | 1 | 1 | 0% | 1,531 | 2,386 | +56% | 0 | 0 | — |
case-12 | pass→pass | 8,245 | 6,588 | -20% | 1 | 1 | 0% | 1,360 | 3,076 | +126% | 0 | 0 | — |
case-13 | fail→pass | 14,426 | 10,379 | -28% | 1 | 1 | 0% | 2,516 | 3,751 | +49% | 0 | 0 | — |
case-14 | fail→pass | 18,284 | 4,929 | -73% | 1 | 1 | 0% | 2,858 | 2,871 | +0% | 0 | 0 | — |
case-15 | fail→pass | 13,743 | 5,972 | -57% | 1 | 1 | 0% | 2,001 | 2,913 | +46% | 0 | 0 | — |
case-16 | fail→pass | 9,616 | 2,922 | -70% | 1 | 1 | 0% | 1,564 | 2,478 | +58% | 0 | 0 | — |
case-17 | fail→pass | 12,896 | 2,635 | -80% | 1 | 1 | 0% | 2,178 | 2,440 | +12% | 0 | 0 | — |
case-18 | fail→pass | 14,017 | 5,047 | -64% | 1 | 1 | 0% | 2,174 | 2,850 | +31% | 0 | 0 | — |
case-19 | pass→pass | 20,462 | 1,573 | -92% | 1 | 1 | 0% | 4,006 | 2,243 | -44% | 0 | 0 | — |
case-20 | fail→pass | 11,648 | 2,477 | -79% | 1 | 1 | 0% | 2,110 | 2,378 | +13% | 0 | 0 | — |
case-21 | pass→fail | 18,966 | 12,464 | -34% | 1 | 1 | 0% | 3,035 | 4,011 | +32% | 0 | 0 | — |
case-22 | pass→pass | 10,427 | 7,968 | -24% | 1 | 1 | 0% | 2,167 | 3,496 | +61% | 0 | 0 | — |
case-23 | pass→fail | 10,229 | 9,294 | -9% | 1 | 1 | 0% | 2,017 | 3,384 | +68% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 22 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +43 percentage points is the difference between those two pass rates over the 22 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.