Prompt Score v3.0 Endpoints
The v3 scoring API replaces the v2.1 composite (Speed / Skill / Efficiency built from SSE, PES, IE, CRR, FC). The headline metric is the Prompt Score — the Average Prompt Grade (APG) on a 0–100 scale (see /insight/summary). The completion rate — the share of qualifying sub-sessions that crushed their goal — rides alongside it as a supporting number (the /score endpoint’s prism_score field).
A sub-session “crushes” (counts toward the completion rate) when all three hold:
- It passes the substance floor (enough real work happened), and
- The outcome judge marks
goal_complete, and - It’s not rework of an earlier attempt.
Intent classification is a separate eligibility gate, not a crush condition: a sub-session the rubric judge couldn’t classify drops out of the score entirely rather than counting as a non-crush.
The scored unit is prism.score_v3_sub_sessions (a goal arc within a Claude Code session). Score and report endpoints here are scoped to the authenticated developer. /v1/model-catalog is the exception: it is an internal-secret-only, global catalog read for trusted services and is not developer-filtered.
Authentication and personal scope
Section titled “Authentication and personal scope”Human developer keys are checked on every request against their active credential lifecycle and the developer’s current organization membership. The API does not promise positive authorization caching. Internal calls without a delegated user are service principals and cannot use personal endpoints; an internally delegated user must also be a current member of the requested organization.
Personal lists, details, edits, disputes, realtime snapshots, and integrity endpoints are scoped to the authenticated organization and developer. A resource ID outside that scope returns 404, rather than disclosing its existence. For sub-session resources, the child and its parent session must carry the same authenticated organization and developer scope.
Claude session IDs remain globally unique; uniqueness has not been relaxed to scope-local composite uniqueness. A request that reuses an existing Claude session ID across scopes fails closed: it neither overwrites nor attaches to that session.
Shared parameters
Section titled “Shared parameters”Most v3 endpoints take a window query parameter:
| Value | Meaning |
|---|---|
daily | Current calendar day |
weekly | Current calendar week (default if omitted) |
monthly | Current calendar month |
Invalid values return 400 Bad Request.
Model catalog
Section titled “Model catalog”GET /v1/model-catalog
Section titled “GET /v1/model-catalog”Internal consumers use this endpoint for the compiled, exact-lookup model catalog. It returns the newest validated snapshot. If the newest stored revision is invalid, the API continues down the revision history until it finds a validated snapshot; it never serves an invalid revision or a fixture/default catalog.
The current catalog document version is schema_version: 1. A successful response includes schema_version, catalog_revision, checksum_sha256, published_at, refreshed_at, stale, and exact_lookups. During a refresh failure, the last validated snapshot can remain available with stale: true; it is not replaced by unvalidated data.
When no validated snapshot exists, the endpoint returns 503 Service Unavailable:
{ "status": "unavailable", "reason": "no validated catalog snapshot"}The same condition makes /health/ready return 503 Service Unavailable; readiness requires both the service lifecycle and a catalog snapshot.
The completion score
Section titled “The completion score”GET /v1/score_v3/score
Section titled “GET /v1/score_v3/score”The single number plus a small sparkline.
GET /v1/score_v3/score?window=weeklyAuthorization: Bearer prism_your_keyResponse:
{ "window": "weekly", "prism_score": 62.5, "letter_grade": "B", "personal_tier": "Proficient", "crushed_count": 5, "total_count": 8, "crush_weight": 0.94, "sparkline": [55.0, 60.0, 58.0, 62.5]}prism_score (field name kept for wire compatibility) is the completion measure — the % of qualifying sub-sessions that crushed their goal. It is not the headline Prompt Score. The headline Prompt Score (Average Prompt Grade, 0–100) comes from apg_current on GET /v1/score_v3/insight/summary. sparkline is the last four windows in chronological order.
Speed pillar
Section titled “Speed pillar”GET /v1/score_v3/speed
Section titled “GET /v1/score_v3/speed”Crushed Sub-sessions Per Window (CSPW), the delta vs the previous window, and median Time-To-Crush.
GET /v1/score_v3/speed?window=weeklyResponse:
{ "window": "weekly", "cspw": 2.4, "delta_cspw": 0.3, "ttc_median_seconds": 1420.0, "sparkline": [1.8, 2.0, 2.1, 2.4]}GET /v1/score_v3/speed/sessions
Section titled “GET /v1/score_v3/speed/sessions”Per-session list for the Speed page — one row per scored sub-session in the window, with rubric and outcome fields.
GET /v1/score_v3/sessions/:id/prompts
Section titled “GET /v1/score_v3/sessions/:id/prompts”Per-prompt breakdown for one v3 sub-session. Joins captured prompts that fall inside the sub-session’s time window with prompt-level rubric output (left join — prompts without a rubric still show).
Tokens pillar
Section titled “Tokens pillar”GET /v1/score_v3/tokens
Section titled “GET /v1/score_v3/tokens”Crush Weight, Token Efficiency Trend (TET), Tokens Per Turn (TPT), and the input / output / cache breakdown.
GET /v1/score_v3/tokens?window=weeklyResponse:
{ "window": "weekly", "crush_weight": 0.94, "tet": 1.12, "tpt": 18450.0, "cpcs_estimate": 0.42, "sparkline": [0.88, 0.91, 0.93, 0.94], "breakdown": { "input": 120000, "output": 28000, "cache_read": 410000, "cache_write": 18000 }}GET /v1/score_v3/tokens/trend
Section titled “GET /v1/score_v3/tokens/trend”Trend series for the tokens page.
Skill pillar
Section titled “Skill pillar”GET /v1/score_v3/skill
Section titled “GET /v1/score_v3/skill”The habit-based Skill Index (0–100) and its Skill tier, plus per-boolean rubric averages.
GET /v1/score_v3/skill?window=weeklyResponse:
{ "window": "weekly", "skill_index": 58.0, "tier": "Expert", "delta_apg": null, "per_boolean": { "goal_explicit": 0.81, "scope_bounded": 0.65, "references_concrete": 0.78, "context_sufficient": 0.7, "verification_requested": 0.55, "root_cause_oriented": 0.62, "plan_first": 0.58 }, "worst_pillar": "verification_requested"}skill_index is null until there are scored prompts in the window; tier is
one of Novice / Practitioner / Proficient / Expert / Elite. delta_apg is
retained in the response shape but is not currently populated (always null).
Insight (Prompt Grade)
Section titled “Insight (Prompt Grade)”The Insight section powers the Prompt Grade page — it’s the per-sub-session rubric (seven booleans + intent class + judge status + letter grade).
GET /v1/score_v3/insight/sessions
Section titled “GET /v1/score_v3/insight/sessions”Recent rubric rows for the authenticated developer.
GET /v1/score_v3/insight/sessions?limit=50Returns an array of RubricItem objects with score_v3_sub_session_id, intent_class, letter_grade, confidence, the seven booleans, applicability, judge_status, and the sub-session title / summary / title_source.
GET /v1/score_v3/insight/summary
Section titled “GET /v1/score_v3/insight/summary”Aggregate summary across the requested window. Its apg_current field is the
headline Prompt Score — the Average Prompt Grade (0–100) for the range —
alongside apg_previous, sample_count, the grade distribution, and a
per-period points trend.
GET /v1/score_v3/insight/sessions_range
Section titled “GET /v1/score_v3/insight/sessions_range”Rubric rows inside an explicit from / to range.
POST /v1/score_v3/insight/backfill
Section titled “POST /v1/score_v3/insight/backfill”Admin trigger to backfill rubric output over a window.
Trivia
Section titled “Trivia”Sessions excluded from scoring because they didn’t pass the substance floor (too short, no real work, etc.). Surfaced so developers can see what was filtered.
GET /v1/score_v3/trivia
Section titled “GET /v1/score_v3/trivia”GET /v1/score_v3/trivia?window=weeklyReturns an array of TriviaItem with score_v3_sub_session_id, reason, created_at, plus title / summary.
GET /v1/score_v3/trivia/trend
Section titled “GET /v1/score_v3/trivia/trend”Trend counts of excluded sessions.
Integrity (anti-gaming flags)
Section titled “Integrity (anti-gaming flags)”GET /v1/score_v3/integrity
Section titled “GET /v1/score_v3/integrity”Flags grouped by kind for the requested window.
GET /v1/score_v3/integrity?window=weeklyResponse:
{ "by_kind": [ { "kind": "auto_retry_storm", "count": 2 }, { "kind": "tiny_prompt_burst", "count": 1 } ]}GET /v1/score_v3/integrity/trend
Section titled “GET /v1/score_v3/integrity/trend”Per-period totals and per-kind counts for charting.
GET /v1/score_v3/integrity/trend?from=2026-04-01T00:00:00Z&to=2026-04-30T00:00:00Z&period=dayGET /v1/score_v3/integrity/recent
Section titled “GET /v1/score_v3/integrity/recent”Recent flag rows for the drill-down list.
GET /v1/score_v3/integrity/recent?from=2026-04-01T00:00:00Z&limit=50Peer comparison
Section titled “Peer comparison”GET /v1/score_v3/peer-comparison
Section titled “GET /v1/score_v3/peer-comparison”Returns a comparison for the authenticated developer over an optional from / to range (and optional tz for daily bucketing).
myScore is the completion rate in percent: crushed_substantive_sessions / substantive_sessions. It is distinct from the APG (Average Prompt Grade) shown by the Skill endpoint.
GET /v1/score_v3/peer-comparison?from=2026-04-01T00:00:00Z&to=2026-04-30T00:00:00ZResponse interpretation:
basis: "peers"means at least four scored peers are available.distribution.peerCountexcludes the caller;cohortMean,p25,p50, andp75summarize everyone in the range with a score, including the caller whenmyScoreis present.myPercentileis the percentage of scored peers whose score is at or below the caller’s.basis: "selfHistory"means a peer distribution cannot be supported, but the caller has at leastrequiredDaysscored daily values.baselinesupplies the number of days, median (typical), latest value, anddelta.basis: "insufficient"means neither comparison has enough supporting data.myScoremay still benullwhen the caller has no scored substantive session in the requested range.
The peer cohort is derived from the caller’s active team memberships and intersects team membership with current organization membership; inactive teams and former organization members are excluded. The client does not select a team.
The response contains no peer names or individual rows. It does expose peerCount, mean, quartiles, the caller’s score, and percentile, so small-group inference may still be possible. It does not provide anonymity or differential-privacy guarantees.
The current API response emits the canonical completionRate / percent / crushed_substantive_sessions / substantive_sessions metric / unit / definition metadata. The Dashboard still accepts a legacy payload with all three fields absent; partial or unsupported metadata is shown as unavailable/unknown rather than as a numeric peer comparison.
Sub-session management
Section titled “Sub-session management”PATCH /v1/score_v3/sub-sessions/:id
Section titled “PATCH /v1/score_v3/sub-sessions/:id”Rename a sub-session (and optionally rewrite its summary). Bumps title_source to "user_edited" so future LLM rewrites won’t overwrite the developer’s choice. Ownership is enforced — a sub-session owned by another developer returns 404.
PATCH /v1/score_v3/sub-sessions/<id>Authorization: Bearer prism_your_keyContent-Type: application/json
{ "title": "Fix auth flow timeout", "summary": "Investigated the 30s timeout in the login redirect and added a retry."}GET /v1/score_v3/realtime/sub-sessions
Section titled “GET /v1/score_v3/realtime/sub-sessions”Snapshot endpoint the dashboard hits on every prism.score_v3.sub_session.* SSE event. Returns recent v3 sub-sessions for the authenticated developer, ordered by started_at DESC, joined to the parent score_v3_sessions row for claude_session_id.
GET /v1/score_v3/realtime/sub-sessions?limit=200&from=2026-05-26T00:00:00Zv3 reports
Section titled “v3 reports”Report generation has its own v3 endpoint family. Same shape as the v2.1 report endpoints (see Insights & Reports) but scoped to v3 scoring data.
| Endpoint | Method | Purpose |
|---|---|---|
/v1/score_v3/report | GET | Latest v3 report |
/v1/score_v3/report/generate | POST | Generate a new v3 report |
/v1/score_v3/report/history | GET | Past reports |
/v1/score_v3/report/pending | GET | In-progress report, or 200 {"report_id": null} |
/v1/score_v3/report/status/:id | GET | Status of a running job |
/v1/score_v3/report/cancel/:id | POST | Cancel a running job |
/v1/score_v3/report/:id | GET / DELETE | Fetch or delete one report |
Report-generation failure status
Section titled “Report-generation failure status”A failed GET /v1/score_v3/report/status/:id response includes the stable machine-readable error_code when the additive column is available. When the report source cannot be read, it is exactly "source_unavailable":
{ "report_id": "<id>", "status": "failed", "error_code": "source_unavailable", "error_message": "Report data is temporarily unavailable. Please retry."}The Dashboard may safely offer report generation again for this failure. The endpoint exposes a fixed safe error_message, not a raw internal source or database error. Other failed statuses have error_code: null and the generic safe retry message. Before the additive column is applied, the compatibility path also returns the generic safe message with error_code: null. The API emits snake_case error_code; the Dashboard accepts the camelCase spelling too when consuming compatible responses.
Canonical daily section range
Section titled “Canonical daily section range”GET /v1/score_v3/files, /prompts/top, /task-time, and /model-task accept additive from_us, to_us, and timezone query parameters. from_us and to_us must be supplied together and define an exact half-open [from_us, to_us) interval; timezone must be an IANA name such as Asia/Seoul or America/Los_Angeles. Each response echoes the accepted bounds in camelCase range: { fromUs, toUs, timezone }.
The daily report Dashboard derives one range from the report’s meta.periodStart and meta.periodEnd, sends that same range to every live section (including /v1/telemetry/analytics), and does not merge a section whose echo differs. Date-only requests remain available for compatibility and derive local midnight boundaries in the supplied timezone. A response without a range echo is accepted only when the Dashboard is explicitly built with NEXT_PUBLIC_SCORE_V3_LEGACY_DAILY_RANGE=true.
Team dashboard data
Section titled “Team dashboard data”GET /v1/score_v3/team/cost
Section titled “GET /v1/score_v3/team/cost”This team-scoped endpoint accepts team_id, window (default weekly), and optional tz (UTC when omitted). Its Parquet-backed cost response uses camelCase fields, including isPartial, prevIsPartial, readCoverage, prevReadCoverage, costRankingAvailable, rollup, and members.
isPartial: true means a bounded file or byte read budget dropped telemetry. Exact aggregate and member numeric fields are then null, spend ranking and spike conclusions are unavailable, and only a positive observed amount may appear as rollup.lowerBoundCostUsd. A zero is exact only for a complete read with no activity. readCoverage and prevReadCoverage identify the budget reason plus read-batch and dropped-file counts; an incomplete previous read also makes prevCostUsd unavailable.
This contract is distinct from catalog pricing coverage. pricingCoverage reports whether requests could be priced; a complete read can still contain unpriced requests, while rollup.isEstimate is always true. The fields are additive or nullable, so an older API’s numeric values remain accepted by the Dashboard during rollback.
GET /v1/score_v3/team/summary
Section titled “GET /v1/score_v3/team/summary”The team summary accepts team_id, optional date (YYYY-MM-DD, otherwise the current UTC date), optional lang (en by default; values beginning with ko select Korean), and optional tz. tz must be a valid IANA timezone such as Asia/Seoul; missing or blank values fall back to UTC, while an invalid name returns 400 Bad Request. The selected date covers local midnight through the next local midnight in that timezone, converted to UTC for comparison with stored timestamps. When generation succeeds, narrative is a normalized, length-capped aggregate plain-text summary and narrativeSource is "llm"; generatedAt records when that text was created.
{ "narrative": "The team completed retry fixes and follow-up work.", "structuredNarrative": null, "narrativeSource": "llm", "generatedAt": "2026-08-06T00:00:00Z"}structuredNarrative remains a nullable wire-compatibility field. A successful prose narrative does not require it, and the Dashboard renders narrative as literal plain text when it is null. A present but malformed or unsupported structured value still fails closed and does not fall back to narrative.
The model receives only sanitized abstract task labels separated by anonymous member boundaries. It does not receive developer IDs or display names, raw prompts, grades, scores, ranks, relative volume, or authorization data. Generated narrative is optional: gateway errors, timeouts, missing content, and empty normalized output leave it unavailable without affecting the live counts. Those failures are not cached. generatedAt exposes the content age. Changed facts can lag the generated narrative by up to four hours; unchanged facts may reuse older content. narrativeInputClipped means the narrative covers the busiest members or tasks, not necessarily the whole team.