Reference
Endpoint reference
Create a short-lived demo JWT. This utility is unauthenticated and only accepts tenantId=demo.
Request and query parameters
| Name | Type | Required | Description |
|---|
tenantId | TEXT enum | Required | Must be demo. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
token | JWT string | required | Bearer token scoped to the demo tenant. | dictionary |
tenantId | TEXT | required | Tenant claim inside the token. | dictionary |
expiresIn | INTEGER | required | Seconds until the token expires. | dictionary |
cURL
curl -sS -X POST "$ANALYTICS_BASE_URL/dev/mint?tenantId=demo"
JavaScript
const response = await fetch(`${base}/dev/mint?tenantId=demo`, { method: "POST" });
const { token } = await response.json();
Example response
{
"token": "demo.analytics.jwt",
"tenantId": "demo",
"expiresIn": 3600
}
GET
List event time facts
/alpha/analytics/v1/event-time-facts
Read signed active, inactive, and waste-second contributions written when source Events close. Use this for audit and low-level time tracing.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. Omit only for authorized cohort reads. |
subjectId | subject_id enum | Optional | Canonical subject after server-side subject normalization. |
startDate | DATE | Optional | Inclusive start date in the school/reporting timezone. |
endDate | DATE | Optional | Exclusive end date. Must be later than startDate. |
modifiedSince | TIMESTAMPTZ | Optional | UTC ISO-8601 polling checkpoint over modified_at. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor returned by the previous page. |
factKind | fact_kind enum | Optional | active_seconds, inactive_seconds, or waste_seconds. |
sourceEventId | TEXT | Optional | Events Alpha event id that produced the fact. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
source_event_id | TEXT | required | Events Alpha event id this contribution derives from. Must reference one closed Events row in the same tenant. | dictionary |
fact_kind | TEXT enum | required | Which time bucket this signed contribution belongs to. Allowed values: fact_kind enum. Set by the versioned time-classification policy named in policy_ref, for example alpha.policy.analytics.time_classification.v2026-06-10. | dictionary |
seconds_delta | NUMERIC(14,3) | required | Signed seconds contributed by this source event to the fact_kind bucket. May be negative for correction/reversal rows; ordinary display measures use rollup fields, not client-side clamping. | dictionary |
subject_id | TEXT | nullable where the row is all-subject or the source cannot validly resolve a subject | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
application_ref | TEXT | nullable | Application/activity-source reference resolved from the source event when available. Reference only; application identity/lifecycle belongs to Applications. | dictionary |
policy_ref | TEXT | required | Named policy/config version used to compute the measure or rollup. Must point at alpha.policy.analytics.* or an inherited Curriculum/Results policy active for the source effective date. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/event-time-facts?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&factKind=active_seconds&startDate=2026-05-01&endDate=2026-06-01" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/event-time-facts`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/event-time-facts",
"data": [
{
"id": "8e9c0ef2-bc75-4f53-8124-df77df49f25f",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"source_event_id": "evt_alpha_000891",
"fact_kind": "active_seconds",
"seconds_delta": 420,
"subject_id": "math",
"application_ref": "app_math_academy",
"policy_ref": "alpha.policy.analytics.time_classification.v2026-06-10",
"quality_status": "ok",
"modified_at": "2026-05-14T18:32:22Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/event-time-facts"
}
}
/alpha/analytics/v1/time-windows
Read active, inactive, waste, and display minutes by student, subject, and reporting window.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. Omit only for authorized cohort reads. |
subjectId | subject_id enum | Optional | Canonical subject after server-side subject normalization. |
startDate | DATE | Optional | Inclusive start date in the school/reporting timezone. |
endDate | DATE | Optional | Exclusive end date. Must be later than startDate. |
modifiedSince | TIMESTAMPTZ | Optional | UTC ISO-8601 polling checkpoint over modified_at. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor returned by the previous page. |
windowKind | window_kind enum | Optional | day, week, term, school_year, or custom. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
subject_id | TEXT | nullable where the row is all-subject or the source cannot validly resolve a subject | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
window_kind | TEXT enum | required | Named window family for this rollup. Allowed values: window_kind enum; custom requires explicit policy/window metadata. | dictionary |
window_start | DATE | required | Inclusive start date of the reporting window in the school timezone. Must be before window_end; ISO date. | dictionary |
window_end | DATE | required | Exclusive end date of the reporting window in the school timezone. Must be after window_start; ISO date. | dictionary |
active_seconds_total | NUMERIC(14,3) | required | Net active seconds from current event_time_fact rows in this window. Can include signed corrections; display fields apply policy presentation. | dictionary |
inactive_seconds_total | NUMERIC(14,3) | required | Net inactive seconds from current event_time_fact rows in this window. Can include signed corrections. | dictionary |
waste_seconds_total | NUMERIC(14,3) | required | Net waste/gaming seconds from current event_time_fact rows in this window. Can be positive or negative after corrections. | dictionary |
display_active_minutes | NUMERIC(12,3) | required | Policy-defined active minutes shown in reports for this time window. Computed by Analytics from active_seconds_total under policy_ref; consumers read it directly. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/time-windows?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&subjectId=math&windowKind=day&startDate=2026-05-01&endDate=2026-06-01" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/time-windows`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/time-windows",
"data": [
{
"id": "1b0d98de-8f58-4ec3-9f2b-5cfbfa3d0001",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"subject_id": "math",
"window_kind": "day",
"window_start": "2026-05-14",
"window_end": "2026-05-15",
"active_seconds_total": 3240,
"inactive_seconds_total": 120,
"waste_seconds_total": 0,
"display_active_minutes": 54,
"quality_status": "ok",
"modified_at": "2026-05-14T23:59:59Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/time-windows"
}
}
GET
List minutes per enrolled school day
/alpha/analytics/v1/school-day-minutes
Read active minutes, enrolled-school-day denominator, MAP-day exclusions, and null_reason when the calendar prerequisite is not yet materialized.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. Omit only for authorized cohort reads. |
subjectId | subject_id enum | Optional | Canonical subject after server-side subject normalization. |
startDate | DATE | Optional | Inclusive start date in the school/reporting timezone. |
endDate | DATE | Optional | Exclusive end date. Must be later than startDate. |
modifiedSince | TIMESTAMPTZ | Optional | UTC ISO-8601 polling checkpoint over modified_at. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor returned by the previous page. |
schoolId | TEXT | Optional | People and Orgs school id used for the calendar denominator. |
includeUnavailable | BOOLEAN | Optional | When true, include current source_missing or policy_pending rows for UI explanation. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
school_id | TEXT | required | People and Orgs school whose calendar/enrollment intersection defines the denominator. Must resolve as the student school for the effective range; no current-only shortcut. | dictionary |
subject_id | TEXT | nullable; null means all reportable subjects | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
window_start | DATE | required | Inclusive start date for the denominator/numerator window. ISO date; half-open range with window_end. | dictionary |
window_end | DATE | required | Exclusive end date for the denominator/numerator window. ISO date; must be after window_start. | dictionary |
active_minutes | NUMERIC(12,3) | required | Policy-defined active minutes for the same window and subject grain. Normalized by the timeback-xp-time-accuracy adapter from reporting.processed_facts.active_seconds, then stored as minutes under policy_ref. | dictionary |
enrolled_school_day_count | INTEGER | required when null_reason=none; nullable only for source_missing repair rows | Denominator: school days intersecting the student effective-dated enrollment and subject assignment when subject_id is present. Nonnegative; excludes weekends, holidays, teacher-workshop days, MAP-testing days, and non-time-locatable enrollments. Null only with a typed repair reason such as source_missing. | dictionary |
excluded_school_day_count | INTEGER | required when null_reason=none; nullable only for source_missing repair rows | Calendar dates inside the requested window that the school-day policy excluded from the denominator. Nonnegative; includes weekend, holiday_break, teacher_workshop, nwea_map_testing, and governed other exclusions. Null only with a typed repair reason such as source_missing. | dictionary |
minutes_per_enrolled_school_day | NUMERIC(12,3) | nullable when denominator is zero or policy/source is missing | Report-ready average active minutes per enrolled school day when the school-day denominator source is available. Formula: round(active_minutes / enrolled_school_day_count, 3) when enrolled_school_day_count > 0 and null_reason=none. Null requires null_reason other than none; consumers never recompute the denominator or substitute active days. | dictionary |
school_day_policy_ref | TEXT | required | Named policy/config version used to compute the measure or rollup. Must point at alpha.policy.analytics.* or an inherited Curriculum/Results policy active for the source effective date. | dictionary |
null_reason | TEXT enum | required | Why minutes_per_enrolled_school_day and denominator counts are null or not reportable. Allowed values: null_reason enum. none means the metric is populated; source_missing means the required calendar source does not exist or is unavailable and the row is not reportable. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this school-day denominator row. Allowed values: quality_status enum. Ordinary Learning Report rows are ok or corrected; source_missing is a readable repair state when the calendar source is unavailable. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/school-day-minutes?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&subjectId=math&startDate=2026-05-01&endDate=2026-06-01&includeUnavailable=true" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/school-day-minutes`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/school-day-minutes",
"data": [
{
"id": "2d4b32cd-c4d8-4e45-8a3a-9de8ce3b0002",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"school_id": "school_alpha_austin",
"subject_id": "math",
"window_start": "2026-05-01",
"window_end": "2026-06-01",
"active_minutes": 1080,
"enrolled_school_day_count": 20,
"excluded_school_day_count": 2,
"minutes_per_enrolled_school_day": 54,
"school_day_policy_ref": "alpha.policy.school_day.v2026-06-10",
"null_reason": "none",
"quality_status": "ok",
"modified_at": "2026-06-01T01:00:00Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/school-day-minutes"
}
}
/alpha/analytics/v1/xp-rollups
Read report-ready XP totals, goals, remaining XP, and signed correction subtotals while Results remains the immutable XP ledger.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. Omit only for authorized cohort reads. |
subjectId | subject_id enum | Optional | Canonical subject after server-side subject normalization. |
startDate | DATE | Optional | Inclusive start date in the school/reporting timezone. |
endDate | DATE | Optional | Exclusive end date. Must be later than startDate. |
modifiedSince | TIMESTAMPTZ | Optional | UTC ISO-8601 polling checkpoint over modified_at. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor returned by the previous page. |
windowKind | window_kind enum | Optional | day, week, term, school_year, or custom. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
subject_id | TEXT | nullable where the row is all-subject or the source cannot validly resolve a subject | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
window_kind | TEXT enum | required | Named window family for the XP rollup. Allowed values: window_kind enum. | dictionary |
window_start | DATE | required | Inclusive start date for included XP ledger effective dates. ISO date; half-open with window_end. | dictionary |
window_end | DATE | required | Exclusive end date for included XP ledger effective dates. ISO date; must be after window_start. | dictionary |
positive_xp | NUMERIC(14,3) | required | Positive XP contribution subtotal retained for audit/explanation. Nonnegative; normalized by the adapter/materializer, with Results xp_ledger refs when available. | dictionary |
negative_xp | NUMERIC(14,3) | required | Sum of negative XP penalties/reversals in this rollup. Zero or negative; gaming/cheating signals and reversals stay signed. | dictionary |
xp_total | NUMERIC(14,3) | required | Policy-defined report XP total after the report-source adapter applies subject folds, exclusions, corrections, and signed contributions. Non-null values require source_import_id provenance; should equal the report value for this student/subject/window. | dictionary |
net_xp | NUMERIC(14,3) | required | Signed audit total after positive and negative XP contributions; equal to xp_total for ordinary report rows under the current policy. positive_xp + negative_xp under policy_ref; retained for correction/reversal reconciliation. | dictionary |
xp_goal | NUMERIC(14,3) | required when null_reason=none; nullable only when enrolled_school_day_count or XP goal policy is missing | Window XP target for this student, subject, and window, used by the Learning Report XP Remaining and XP Goal Percent panels. Nonnegative when populated; equals daily_xp_goal multiplied by enrolled_school_day_count when both are present. Null requires a typed repair reason such as source_missing or policy_pending. | dictionary |
daily_xp_goal | NUMERIC(14,3) | nullable when daily XP policy is missing | Daily XP target used in the auditor-pinned XP goal percent denominator. Nonnegative; read from alpha.policy.analytics.xp_goal_percent.v1 / Curriculum Policy, never from a consumer constant. | dictionary |
enrolled_school_day_count | INTEGER | required when null_reason=none; nullable only when school-day denominator source is missing | Number of days in the XP percent denominator: days the student was enrolled and school was in session. Nonnegative integer when populated. Enrollment begin/end bound the count; the student's school calendar governs it; MAP testing days are excluded; per-subject rows count only days with a subject assignment. Null requires a typed repair reason such as source_missing. | dictionary |
xp_remaining | NUMERIC(14,3) | required when null_reason=none; nullable when xp_goal is null/source_missing | Report-ready remaining XP under policy_ref. max(xp_goal - xp_total, 0) under policy_ref when xp_goal is present; stored by Analytics so reports do not subtract locally. Null requires a typed repair reason. | dictionary |
xp_goal_percent | NUMERIC(9,3) | nullable when daily_xp_goal or enrolled_school_day_count is null or zero | Uncapped percent of the XP goal reached in this window when the school-day denominator is available. Formula under alpha.policy.analytics.xp_goal_percent.v1 when populated: xp_total / (daily_xp_goal x enrolled_school_day_count) x 100. Values above 100 are valid and must remain visible. Null requires a typed repair reason. | dictionary |
xp_completion_percent | NUMERIC(9,3) | deprecated compatibility alias; nullability matches xp_goal_percent | Deprecated compatibility alias for xp_goal_percent. Must equal xp_goal_percent exactly and remains uncapped. It has no independent capped-completion meaning. Nullability matches xp_goal_percent. | dictionary |
on_track | BOOLEAN | nullable when xp_goal_percent is null | Whether the student is on track against the XP goal policy for this window. Computed under policy_ref; null means the goal policy or denominator is missing. | dictionary |
award_count | INTEGER | required | Number of positive or neutral XP ledger rows contributing to this rollup. Nonnegative; excludes superseded rows unless audit query requests them. | dictionary |
reversal_count | INTEGER | required | Number of reversal/correction XP ledger rows included. Nonnegative. | dictionary |
source_import_id | TEXT | required for report-tile rows; nullable for purely derived event-axis rows | Analytics source-import receipt that proves which named report-source adapter produced this report-grade fact. Must reference alpha.analytics_source_import for non-null Learning Report tile values; no literal or fixture value may be reportable without this provenance. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
null_reason | TEXT enum | required | Why XP goal fields are null or unavailable while xp_total itself may still be reportable. Allowed values: null_reason enum. none means xp_goal, xp_remaining, xp_goal_percent, and on_track are populated; source_missing means the shared school-day denominator source is absent and the goal fields are not reportable. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/xp-rollups?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&subjectId=math&windowKind=term&startDate=2026-01-01&endDate=2026-06-01" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/xp-rollups`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/xp-rollups",
"data": [
{
"id": "3f2b79e1-6182-4e21-a0b6-7d6cf7530003",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"subject_id": "math",
"window_kind": "term",
"window_start": "2026-01-01",
"window_end": "2026-06-01",
"positive_xp": 11850,
"negative_xp": -50,
"xp_total": 11800,
"net_xp": 11800,
"xp_goal": 9000,
"daily_xp_goal": 100,
"enrolled_school_day_count": 90,
"xp_remaining": 0,
"xp_goal_percent": 131.111,
"xp_completion_percent": 131.111,
"on_track": true,
"award_count": 24,
"reversal_count": 1,
"source_import_id": "imp_01HT8XPTIME_2026_SPRING",
"quality_status": "ok",
"null_reason": "none",
"modified_at": "2026-06-01T01:02:00Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/xp-rollups"
}
}
GET
List accuracy rollups
/alpha/analytics/v1/accuracy-rollups
Read report-ready correct questions, total questions, incorrect questions, and accuracy percent without client-side processed_facts math.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. Omit only for authorized cohort reads. |
subjectId | subject_id enum | Optional | Canonical subject after server-side subject normalization. |
startDate | DATE | Optional | Inclusive start date in the school/reporting timezone. |
endDate | DATE | Optional | Exclusive end date. Must be later than startDate. |
modifiedSince | TIMESTAMPTZ | Optional | UTC ISO-8601 polling checkpoint over modified_at. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor returned by the previous page. |
windowKind | window_kind enum | Optional | day, week, term, school_year, or custom. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
subject_id | TEXT | nullable where the row is all-subject or the source cannot validly resolve a subject | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
window_kind | TEXT enum | required | Named window family for the Accuracy rollup. Allowed values: window_kind enum. | dictionary |
window_start | DATE | required | Inclusive start date for source question rows included in this rollup. ISO date; half-open with window_end. | dictionary |
window_end | DATE | required | Exclusive end date for source question rows included in this rollup. ISO date; must be after window_start. | dictionary |
correct_question_count | INTEGER | required | Reportable correct-question numerator after the adapter applies subject, app/source, score_type, correction, and real-student rules. Nonnegative integer; cannot exceed total_question_count when total_question_count is present. Normalized from source-shaped reporting.processed_facts.correct_questions, not read by reports from the source table. | dictionary |
total_question_count | INTEGER | required | Reportable question-attempt denominator after the adapter applies the same policy filters as correct_question_count. Nonnegative integer. Zero is allowed only with null_reason=no_questions and null accuracy_percent. | dictionary |
incorrect_question_count | INTEGER | required | Reportable incorrect-question count retained for explanations. Nonnegative integer; equals total_question_count - correct_question_count under the policy version. | dictionary |
accuracy_percent | NUMERIC(9,3) | nullable when total_question_count is zero or source/policy is missing | Report-ready percent of reportable questions answered correctly. 0 through 100 when populated; null requires a typed null_reason. Formula and rounding are owned by Analytics under policy_ref. | dictionary |
source_import_id | TEXT | required for report-tile rows; nullable for purely derived event-axis rows | Analytics source-import receipt that proves which named report-source adapter produced this report-grade fact. Must reference alpha.analytics_source_import for non-null Learning Report tile values; no literal or fixture value may be reportable without this provenance. | dictionary |
source_fact_refs | JSONB | required | Compact ids/hashes/cursor for source-shaped processed_facts rows accepted by the adapter. Stores refs/hashes only; never copies raw processed_facts rows or student identifiers. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
null_reason | TEXT enum | required | Why accuracy_percent is null or not reportable. Allowed values: null_reason enum. no_questions means the source window had zero reportable question attempts; none means the metric is populated. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/accuracy-rollups?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&subjectId=math&windowKind=term&startDate=2026-01-01&endDate=2026-06-01" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/accuracy-rollups`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/accuracy-rollups",
"data": [
{
"id": "9c3f3e4f-1fd8-4c28-8a72-cbbf2e6d0006",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"subject_id": "math",
"window_kind": "term",
"window_start": "2026-01-01",
"window_end": "2026-06-01",
"correct_question_count": 480,
"total_question_count": 600,
"incorrect_question_count": 120,
"accuracy_percent": 80,
"source_import_id": "imp_01HT8XPTIME_2026_SPRING",
"source_fact_refs": [
"processed_facts_hash_01HT7Q"
],
"quality_status": "ok",
"null_reason": "none",
"modified_at": "2026-06-01T01:02:30Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/accuracy-rollups"
}
}
/alpha/analytics/v1/mastery-deltas
Read dated changes in Results-owned KC mastery state. The current state remains in Results; Analytics reports what changed in the window.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. |
kcId | TEXT | Optional | Knowledge Component id. |
stateDimension | state_dimension enum | Optional | durable mastery, working grade, highest mastered grade, or another governed dimension. |
transitionKind | transition_kind enum | Optional | How the state changed. |
startDate | DATE | Optional | Inclusive boundary for effective_at. |
endDate | DATE | Optional | Exclusive boundary for effective_at. |
modifiedSince | TIMESTAMPTZ | Optional | Poll for changed rows. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
kc_id | TEXT | nullable when the row is a grade-level mastered fact without a single KC | Knowledge Component whose student state changed. Must reference a Curriculum KC; never a standard or lesson id. | dictionary |
grade_subject | TEXT enum | nullable except grade-level mastered rows | Subject for a Grade Levels Mastered row shown in the Learning Report. Closed Alpha subject enum after write-time normalization; required when grade_level/completed_on are populated. | dictionary |
grade_level | INTEGER | nullable except grade-level mastered rows | Grade level the student has mastered in the subject. Positive integer; set by the grade-mastery adapter from passed gate/assessment evidence. | dictionary |
state_dimension | TEXT enum | required | Which Results mastery-state dimension changed. Allowed values: state_dimension enum. | dictionary |
effective_at | TIMESTAMPTZ | required | When the state transition is effective for as-of reporting. UTC timestamp; comes from Results state transition, not Analytics computed_at. | dictionary |
previous_value | JSONB | nullable | Typed previous value for the changed state dimension. JSON shape must match state_dimension; null allowed for first known state. | dictionary |
new_value | JSONB | required | Typed new value for the changed state dimension. JSON shape must match state_dimension. | dictionary |
delta_value | NUMERIC(12,6) | nullable | Numeric difference where the state dimension has a numeric value. Null for non-numeric dimensions such as next_due_at or fluency_state. | dictionary |
transition_kind | TEXT enum | required | Human-readable category of the state transition. Allowed values: acquired, improved, decayed, review_due, fluency_changed, corrected, blocked. Set by Results/Analytics materializer policy; reports group by this value instead of classifying attempts. | dictionary |
source_result_refs | JSONB | required | Result/evidence ids that caused or explain the state transition or grade-level mastery fact. Stores ids/hashes only; no raw scored payload copies. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/mastery-deltas?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&stateDimension=durable_mastery&startDate=2026-05-01&endDate=2026-06-01" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/mastery-deltas`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/mastery-deltas",
"data": [
{
"id": "4e9268b7-e76a-46d9-9b6e-899aa2df0004",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"kc_id": "kc_math_fraction_addition_unlike_denominators",
"grade_subject": "math",
"grade_level": 5,
"state_dimension": "durable_mastery",
"effective_at": "2026-05-14T18:45:00Z",
"previous_value": {
"value": 0.82
},
"new_value": {
"value": 0.91
},
"delta_value": 0.09,
"transition_kind": "acquired",
"source_result_refs": [
"result_01HT7K8BR3",
"result_kc_evidence_01HT7K8BR3_KC"
],
"quality_status": "ok",
"modified_at": "2026-05-14T18:45:04Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/mastery-deltas"
}
}
GET
List MAP growth rollups
/alpha/analytics/v1/map-growth-rollups
Read report-ready RIT, achievement percentile, growth percentile, Growth X, target, on-track state, sitting count, and retake count.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. |
subjectId | subject_id enum | Optional | Canonical Alpha subject id. |
termId | TEXT | Optional | Public API alias for canonical_term_id. |
growthWindow | growth_window enum | Optional | Use winter_to_winter for the Learning Report vs one-year panel. |
normsSet | norms_set enum | Optional | 2020 or 2025. This selects stored rollup rows; clients do not carry norm tables. |
modifiedSince | TIMESTAMPTZ | Optional | Poll for changed rows. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
subject_id | TEXT | required for MAP rollups | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
canonical_term_id | TEXT | required | Alpha canonical term id for the MAP observation/window. Set by Results/NWEAMap term normalization; not parsed by Analytics clients. | dictionary |
growth_window | TEXT enum | required | MAP growth interval represented by this row. Allowed values: growth_window enum. | dictionary |
norms_set | TEXT enum | required | NWEA norms family used for projected growth and normed outputs. Allowed values: norms_set enum; the same report can be regenerated by changing this filter. | dictionary |
rit_score | NUMERIC(6,2) | required | Report-ready RIT value from Results MAP test-of-record for this subject/term. Positive numeric; no local test-of-record choice in Analytics clients. | dictionary |
achievement_percentile | NUMERIC(6,2) | nullable when the MAP source row has no achievement percentile | Achievement percentile for the test-of-record sitting. 0 to 100 when present; for hp_map_results this is normalized from testpercentile. | dictionary |
growth_percentile | NUMERIC(6,2) | nullable when the selected growth_window has no conditional growth percentile | Conditional growth percentile for the selected MAP window; the Learning Report "vs 1yr ago" panel reads this from the winter_to_winter row. 0 to 100 when present; null requires null_reason for report windows that should have growth. | dictionary |
observed_growth | NUMERIC(7,3) | nullable when no paired window exists | Actual RIT growth for the growth_window. Null for term-only observations; otherwise computed by Results MAP views. | dictionary |
projected_growth | NUMERIC(7,3) | nullable when no projected growth exists | NWEA projected-growth value for the selected window. Must be greater than 0 when growth_x is present; comes from Results/NWEAMap normalized projections and never from a client norms table. | dictionary |
growth_x | NUMERIC(8,4) | nullable when observed or projected growth is unavailable | Observed growth divided by projected_growth, using the named norms_set and growth_window. Null requires null_reason; target comparisons use growth_x_target. | dictionary |
growth_x_target | NUMERIC(8,4) | required | Alpha target Growth X for intervention/on-track decisions. Normally 2 under current Alpha policy, but read from policy_ref. | dictionary |
on_track | BOOLEAN | nullable when growth_x or target is null | Whether the student is meeting the Growth X target for this row. Computed under policy_ref; null requires null_reason. | dictionary |
sitting_count | INTEGER | required | Number of MAP sittings Results observed for this subject/term before test-of-record selection. Nonnegative; includes source-visible sittings according to Results policy. | dictionary |
retake_count | INTEGER | required | Number of non-test-of-record sittings represented by the sitting_count. Nonnegative; computed by Results/NWEAMap policy. | dictionary |
source_import_id | TEXT | required for report-tile rows; nullable for purely derived event-axis rows | Analytics source-import receipt that proves which named report-source adapter produced this report-grade fact. Must reference alpha.analytics_source_import for non-null Learning Report tile values; no literal or fixture value may be reportable without this provenance. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/map-growth-rollups?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&subjectId=math&termId=term_2026_winter&growthWindow=winter_to_winter&normsSet=2025" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/map-growth-rollups`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/map-growth-rollups",
"data": [
{
"id": "5a41fb2a-5d08-45fd-a62b-58a518060005",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"subject_id": "math",
"canonical_term_id": "term_2026_winter",
"growth_window": "winter_to_winter",
"norms_set": "2025",
"rit_score": 239,
"achievement_percentile": 91,
"growth_percentile": 43,
"observed_growth": 6,
"projected_growth": 7,
"growth_x": 0.8571,
"growth_x_target": 2,
"on_track": false,
"sitting_count": 2,
"retake_count": 1,
"source_import_id": "imp_01HT8MAP_2026_WINTER",
"quality_status": "ok",
"modified_at": "2026-05-14T19:00:00Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/map-growth-rollups"
}
}
GET
Read the NWEA norms resource
/alpha/analytics/v1/norms
Read the versioned achievement-status norms resource that powers percentile-to-RIT and RIT-to-percentile translation. Same contract as /norms/table; cache by table_version and never ship a private norms table.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Optional | math, reading, language, or science. subjectId is accepted as an alias. |
grade | TEXT | Optional | K or 1 through 12. gradeLevel is accepted as an alias. |
season | TEXT enum | Optional | fall, winter, or spring. termSeason is accepted as an alias. |
role | TEXT | Optional | NWEA role/population label. Defaults to student for public student translations. |
normsSet | norms_set enum | Optional | 2020 or 2025. Omit to list both published sets. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | Always list. | dictionary |
table | TEXT | required | analytics.norms_table list resource over alpha.analytics_norms_achievement. | dictionary |
table_version | TEXT | required | Version of the readable norms table. | dictionary |
data[].id | TEXT | required | Stable Analytics id for this norms row. | dictionary |
data[].norms_set | TEXT enum | required | Norms family for the row. | dictionary |
data[].subject_id | TEXT | required | Canonical subject for the norms row. | dictionary |
data[].source_subject_name | TEXT | required | Published source subject name for audit display. | dictionary |
data[].role | TEXT | required | NWEA role/population label for the row. | dictionary |
data[].grade_key | TEXT | required | NWEA grade key for the row. | dictionary |
data[].grade_level | INTEGER | required | Numeric grade level for sorting and display. | dictionary |
data[].season | TEXT | required | MAP testing season for the row. | dictionary |
data[].mean_rit | NUMERIC(7,2) | required | Published mean RIT for the row. | dictionary |
data[].sd_rit | NUMERIC(6,3) | required | Published RIT standard deviation for the row. | dictionary |
data[].calculator_version | TEXT | required | Version of the surface norms calculator/table. | dictionary |
data[].source_ref | TEXT | required | Opaque provenance pointer to NWEA-published norms. | dictionary |
limit | INTEGER | required | Rows returned in this page. | dictionary |
hasMore | BOOLEAN | required | Whether another page exists. | dictionary |
nextCursor | TEXT nullable | required | Opaque cursor for the next page. | dictionary |
links.self | URL | required | The request URL for this page. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/norms?subject=math&grade=5&season=winter&normsSet=2025&limit=1" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/norms`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"table": "analytics.norms_table",
"table_version": "analytics.norms.achievement.v2026-06-12",
"data": [
{
"id": "norms_2025_math_student_5_winter",
"norms_set": "2025",
"subject_id": "math",
"source_subject_name": "Math",
"role": "student",
"grade_key": "5",
"grade_level": 5,
"season": "winter",
"mean_rit": 211.82,
"sd_rit": 17.42,
"calculator_version": "analytics.norms.achievement.v2026-06-12",
"source_ref": "NWEA 2025 MAP Growth Norms Technical Manual (Hawthorne, Velazquez, Peng, Hall, Newburn, 2025)"
}
],
"limit": 1,
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/norms?subject=math&grade=5&season=winter&normsSet=2025&limit=1"
}
}
GET
Read the filterable NWEA norms table
/alpha/analytics/v1/norms/table
Read the paginated table rows for the current or requested NWEA achievement-status norms table version.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Optional | math, reading, language, or science. subjectId is accepted as an alias. |
grade | TEXT | Optional | K or 1 through 12. gradeLevel is accepted as an alias. |
season | TEXT enum | Optional | fall, winter, or spring. termSeason is accepted as an alias. |
role | TEXT | Optional | NWEA role/population label. Defaults to student for public student translations. |
normsSet | norms_set enum | Optional | 2020 or 2025. Omit to list both published sets. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | Always list. | dictionary |
table | TEXT | required | analytics.norms_table list resource over alpha.analytics_norms_achievement. | dictionary |
table_version | TEXT | required | Version of the readable norms table. | dictionary |
data[].id | TEXT | required | Stable Analytics id for this norms row. | dictionary |
data[].norms_set | TEXT enum | required | Norms family for the row. | dictionary |
data[].subject_id | TEXT | required | Canonical subject for the norms row. | dictionary |
data[].source_subject_name | TEXT | required | Published source subject name for audit display. | dictionary |
data[].role | TEXT | required | NWEA role/population label for the row. | dictionary |
data[].grade_key | TEXT | required | NWEA grade key for the row. | dictionary |
data[].grade_level | INTEGER | required | Numeric grade level for sorting and display. | dictionary |
data[].season | TEXT | required | MAP testing season for the row. | dictionary |
data[].mean_rit | NUMERIC(7,2) | required | Published mean RIT for the row. | dictionary |
data[].sd_rit | NUMERIC(6,3) | required | Published RIT standard deviation for the row. | dictionary |
data[].calculator_version | TEXT | required | Version of the surface norms calculator/table. | dictionary |
data[].source_ref | TEXT | required | Opaque provenance pointer to NWEA-published norms. | dictionary |
limit | INTEGER | required | Rows returned in this page. | dictionary |
hasMore | BOOLEAN | required | Whether another page exists. | dictionary |
nextCursor | TEXT nullable | required | Opaque cursor for the next page. | dictionary |
links.self | URL | required | The request URL for this page. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/norms/table?subject=math&grade=5&season=winter&normsSet=2025&limit=1" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/norms/table`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"table": "analytics.norms_table",
"table_version": "analytics.norms.achievement.v2026-06-12",
"data": [
{
"id": "norms_2025_math_student_5_winter",
"norms_set": "2025",
"subject_id": "math",
"source_subject_name": "Math",
"role": "student",
"grade_key": "5",
"grade_level": 5,
"season": "winter",
"mean_rit": 211.82,
"sd_rit": 17.42,
"calculator_version": "analytics.norms.achievement.v2026-06-12",
"source_ref": "NWEA 2025 MAP Growth Norms Technical Manual (Hawthorne, Velazquez, Peng, Hall, Newburn, 2025)"
}
],
"limit": 1,
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/norms/table?subject=math&grade=5&season=winter&normsSet=2025&limit=1"
}
}
GET
Translate percentile to RIT
/alpha/analytics/v1/norms/rit
Convert a requested achievement percentile to a RIT score using the surface-owned achievement norms table for subject, grade, season, role, and norms set.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Required | math, reading, language, or science. subjectId is accepted as an alias. |
grade | TEXT | Required | K or 1 through 12. gradeLevel is accepted as an alias. |
season | TEXT enum | Required | fall, winter, or spring. termSeason is accepted as an alias. |
role | TEXT | Optional | NWEA role/population label. Defaults to student. |
percentile | NUMERIC | Required | Percentile to translate. Must be greater than 0 and less than 100. |
normsSet | norms_set enum | Required | 2020 or 2025. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | analytics.norms_rit_translation. | dictionary |
direction | TEXT enum | required | percentile_to_rit. | dictionary |
subject_id | TEXT | required | Canonical subject used for the lookup. | dictionary |
grade_key | TEXT | required | Grade key used by the published norms resource. | dictionary |
grade_level | INTEGER | required | Numeric grade level used by the calculator. | dictionary |
season | TEXT | required | fall, winter, spring, or another published NWEA season label. | dictionary |
role | TEXT | required | NWEA role/population label used for the lookup. | dictionary |
percentile | NUMERIC(5,2) | required | Requested percentile. | dictionary |
rit_score | NUMERIC | required | Rounded RIT score returned by the surface calculator. | dictionary |
output_rit | NUMERIC | required | Stable output alias for the rounded RIT score. | dictionary |
raw_rit | NUMERIC | required | Unrounded calculator output, rounded to three decimals. | dictionary |
norms_set | TEXT enum | required | Norms family used for the lookup. | dictionary |
calculator_version | TEXT | required | Version of the surface norms table and calculator used for the normal-model translation. | dictionary |
mean_rit | NUMERIC(7,2) | required | Published mean RIT used by the calculator. | dictionary |
sd_rit | NUMERIC(6,3) | required | Published RIT standard deviation used by the calculator. | dictionary |
source_ref | TEXT | required | Provenance pointer for the published norms row. | dictionary |
links.table | URL | required | The /norms/table resource row used for the lookup. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/norms/rit?subject=math&grade=5&season=winter&percentile=99&normsSet=2025" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/norms/rit`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "analytics.norms_rit_translation",
"direction": "percentile_to_rit",
"norms_set": "2025",
"subject_id": "math",
"grade_key": "5",
"grade_level": 5,
"season": "winter",
"role": "student",
"percentile": 99,
"rit_score": 252,
"output_rit": 252,
"raw_rit": 252.345,
"calculator_version": "analytics.norms.achievement.v2026-06-12",
"mean_rit": 211.82,
"sd_rit": 17.42,
"source_ref": "NWEA 2025 MAP Growth Norms Technical Manual (Hawthorne, Velazquez, Peng, Hall, Newburn, 2025)",
"links": {
"table": "/alpha/analytics/v1/norms/table?subject=math&grade=5&season=winter&normsSet=2025&role=student"
}
}
GET
Translate RIT to percentile
/alpha/analytics/v1/norms/percentile
Convert a RIT score to an achievement percentile using the same surface-owned norms table row.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Required | math, reading, language, or science. subjectId is accepted as an alias. |
grade | TEXT | Required | K or 1 through 12. gradeLevel is accepted as an alias. |
season | TEXT enum | Required | fall, winter, or spring. termSeason is accepted as an alias. |
role | TEXT | Optional | NWEA role/population label. Defaults to student. |
rit | NUMERIC | Required | RIT score to translate. |
normsSet | norms_set enum | Required | 2020 or 2025. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | analytics.norms_percentile_translation. | dictionary |
direction | TEXT enum | required | rit_to_percentile. | dictionary |
subject_id | TEXT | required | Canonical subject used for the lookup. | dictionary |
grade_key | TEXT | required | Grade key used by the published norms resource. | dictionary |
grade_level | INTEGER | required | Numeric grade level used by the calculator. | dictionary |
season | TEXT | required | fall, winter, spring, or another published NWEA season label. | dictionary |
role | TEXT | required | NWEA role/population label used for the lookup. | dictionary |
rit_score | NUMERIC | required | Requested RIT score, rounded for response stability. | dictionary |
input_rit | NUMERIC | required | Requested RIT score. | dictionary |
percentile | NUMERIC(5,2) | required | Achievement percentile returned by the surface calculator. | dictionary |
output_percentile | NUMERIC(5,2) | required | Stable output alias for the achievement percentile. | dictionary |
raw_percentile | NUMERIC | required | Unrounded normal-model percentile, rounded to three decimals. | dictionary |
norms_set | TEXT enum | required | Norms family used for the lookup. | dictionary |
calculator_version | TEXT | required | Version of the surface norms table and calculator used for the normal-model translation. | dictionary |
mean_rit | NUMERIC(7,2) | required | Published mean RIT used by the calculator. | dictionary |
sd_rit | NUMERIC(6,3) | required | Published RIT standard deviation used by the calculator. | dictionary |
source_ref | TEXT | required | Provenance pointer for the published norms row. | dictionary |
links.table | URL | required | The /norms/table resource row used for the lookup. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/norms/percentile?subject=math&grade=5&season=winter&rit=247&normsSet=2025" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/norms/percentile`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "analytics.norms_percentile_translation",
"direction": "rit_to_percentile",
"norms_set": "2025",
"subject_id": "math",
"grade_key": "5",
"grade_level": 5,
"season": "winter",
"role": "student",
"rit_score": 247,
"input_rit": 247,
"percentile": 98,
"output_percentile": 98,
"raw_percentile": 97.828,
"calculator_version": "analytics.norms.achievement.v2026-06-12",
"mean_rit": 211.82,
"sd_rit": 17.42,
"source_ref": "NWEA 2025 MAP Growth Norms Technical Manual (Hawthorne, Velazquez, Peng, Hall, Newburn, 2025)",
"links": {
"table": "/alpha/analytics/v1/norms/table?subject=math&grade=5&season=winter&normsSet=2025&role=student"
}
}
/alpha/analytics/v1/r90/table
Read the single versioned Alpha R90 table used for RIT-to-grade and GOALS track-position work.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Optional | math, reading, language, science, or a locked proxy subject such as vocabulary, writing, or fastmath. subjectId is accepted as an alias. |
normsSet | norms_set enum | Optional | 2020 or 2025. Accepted for caller symmetry; the current R90 table version is returned. |
tableVersion | TEXT | Optional | Immutable R90 table_version. Omit for the current version of the requested normsSet. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | Always list. | dictionary |
table | TEXT | required | analytics.r90_table list resource over alpha.analytics_r90_table. | dictionary |
table_version | TEXT | required | Version of the readable R90 table. | dictionary |
data[].id | TEXT | required | Stable Analytics id for this R90 row. | dictionary |
data[].table_version | TEXT | required | Immutable version id for the row. | dictionary |
data[].table_subject_id | TEXT | required | Canonical table subject for this R90 row. | dictionary |
data[].subject_id | TEXT | required | Subject returned by the table endpoint. | dictionary |
data[].source_subject_name | TEXT | required | Published source subject name for audit display. | dictionary |
data[].rit_score | NUMERIC(7,2) | required | RIT score represented by this row. | dictionary |
data[].r90_grade | TEXT | required | Plain grade-position value for this row. | dictionary |
data[].effective_grade | TEXT | required | Report-ready grade/effective-grade label. | dictionary |
data[].r90_grade_level | TEXT | required | Governed grade-level bucket for this row. | dictionary |
data[].r90_percent_complete | NUMERIC(7,3) | required | Compatibility field for the MAP-inferred percent through the R90 grade band. | dictionary |
data[].rit90_grade_band_percent | NUMERIC(7,3) | required | Clear field name for the MAP-inferred percent through the R90 grade band. This is not actual course or grade-level progress. | dictionary |
data[].observation_count | INTEGER | required | Count carried by the Alpha R90 mirror for audit. | dictionary |
data[].source_ref | TEXT | required | Runtime provenance pointer for the NWEAMAP-owned seeded grade2rit source backing this current table row. It is not a route and clients do not read it to answer reports; cache and reproduce by table_version. | dictionary |
limit | INTEGER | required | Rows returned in this page. | dictionary |
hasMore | BOOLEAN | required | Whether another page exists. | dictionary |
nextCursor | TEXT nullable | required | Opaque cursor for the next page. | dictionary |
links.self | URL | required | The request URL for this page. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/r90/table?subject=math&normsSet=2025&limit=1" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/r90/table`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"table": "analytics.r90_table",
"table_version": "analytics.rit_to_grade.powerpath.v2026-06-15",
"data": [
{
"id": "r90_math_172",
"table_version": "analytics.rit_to_grade.powerpath.v2026-06-15",
"table_subject_id": "math",
"subject_id": "math",
"source_subject_name": "Math",
"rit_score": 172,
"r90_grade": 0,
"effective_grade": 1,
"r90_grade_level": 0,
"r90_percent_complete": 0,
"rit90_grade_band_percent": 0,
"observation_count": 1,
"source_ref": "powerpath:/powerpath/rit-to-grade",
"ownerModule": "nweamap"
}
],
"limit": 1,
"hasMore": true,
"nextCursor": "eyJvZmZzZXQiOjF9",
"links": {
"self": "/alpha/analytics/v1/r90/table?subject=math&normsSet=2025&limit=1"
}
}
GET
Translate RIT to R90 and grade position
/alpha/analytics/v1/r90
Return the R90 grade, effective grade, grade level, and MAP-inferred grade-band percent for a RIT score using the current exact PowerPath RIT-to-grade mirror.
Request and query parameters
| Name | Type | Required | Description |
|---|
subject | subject_id enum | Required | math, reading, language, science, or a locked proxy subject such as vocabulary, writing, or fastmath. subjectId is accepted as an alias. |
rit | NUMERIC | Required | RIT score to translate. Exact table hits return source_point_kind=exact; missing rows return source_point_kind=source_missing rather than interpolation, step-flooring, upward clamping, or extrapolation. |
normsSet | norms_set enum | Optional | 2020 or 2025. Accepted for caller symmetry; R90 table_version is returned in the body. |
tableVersion | TEXT | Optional | Immutable R90 table_version. Omit for the current version of the requested normsSet. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | analytics.r90_lookup. | dictionary |
table_version | TEXT | required | Version of the Alpha R90 mirror table. | dictionary |
requested_subject_id | TEXT | required | Subject requested by the caller before locked proxy folding. | dictionary |
table_subject_id | TEXT | required | Canonical table subject used for the lookup. | dictionary |
subject_id | TEXT | required | Subject returned to the caller after validation. | dictionary |
rit_score | NUMERIC | required | RIT table point used for the response. | dictionary |
table_rit_score | NUMERIC nullable | required | Selected table RIT. Exact lookups return the current-version row whose rit_score equals the requested RIT; missing rows return source_missing. | dictionary |
r90_grade | TEXT | required | Plain grade-position value for the RIT row. | dictionary |
effective_grade | TEXT | required | Report-ready grade/effective-grade label. | dictionary |
r90_grade_level | TEXT | required | Governed grade-level bucket for this R90 row. | dictionary |
r90_percent_complete | NUMERIC(7,3) | required | Compatibility field for the MAP-inferred percent through the R90 grade band. | dictionary |
rit90_grade_band_percent | NUMERIC(7,3) | required | Clear field name for the MAP-inferred percent through the R90 grade band. This is not actual course or grade-level progress. | dictionary |
source_point_kind | TEXT enum | required | exact or source_missing. The current R90 lookup never interpolates, step-floors, clamps, or extrapolates. | dictionary |
calculator_version | TEXT | required | Version of the Alpha R90 mirror table and calculator. | dictionary |
source_ref | TEXT | required | Runtime provenance pointer for the NWEAMAP-owned seeded grade2rit source row used by this lookup. It is not a route and clients do not read it to answer reports; cache and reproduce by table_version. | dictionary |
links.table | URL | required | The /r90/table resource family used for the lookup. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/r90?subject=math&rit=239&normsSet=2025" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/r90`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "analytics.r90_lookup",
"table_version": "analytics.rit_to_grade.powerpath.v2026-06-15",
"requested_subject_id": "math",
"table_subject_id": "math",
"subject_id": "math",
"rit_score": 239,
"table_rit_score": 239,
"r90_grade": 4.8,
"effective_grade": 5,
"r90_grade_level": 4,
"r90_percent_complete": 80,
"rit90_grade_band_percent": 80,
"source_point_kind": "exact",
"calculator_version": "analytics.rit_to_grade.powerpath.v2026-06-15",
"source_ref": "powerpath:/powerpath/rit-to-grade",
"ownerModule": "nweamap",
"links": {
"table": "/alpha/analytics/v1/r90/table?subject=math"
}
}
GET
Read school days remaining
/alpha/analytics/v1/school-days-remaining
Return the forward instructional-day count and XP-to-time effort projection GOALS needs for its target-date column. GOALS asks Analytics for this read instead of doing calendar arithmetic locally.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Required | Canonical Alpha student id. |
schoolId | TEXT | Optional | School id for the calendar denominator. Omit to let Analytics infer the student's enrolled school. |
asOf | DATE | Required | Start date for the forward school-day count. startDate is accepted as an alias. |
endDate | DATE | Optional | Exclusive end date for the forward window. schoolYearEnd is accepted as an alias; omit to use the surface's current GOALS school-year end. |
targetDate | DATE | Optional | Date GOALS wants to test for the target-date column. Must be after asOf and on or before endDate. |
xpRemaining | NUMERIC | Optional | Remaining XP to convert into expected minutes by the surface unit 1 XP = 1 expected minute. Mutually exclusive with minutesRemaining and hoursRemaining. |
minutesRemaining | NUMERIC | Optional | Expected minutes remaining. Mutually exclusive with xpRemaining and hoursRemaining. |
hoursRemaining | NUMERIC | Optional | Expected hours remaining. Mutually exclusive with xpRemaining and minutesRemaining. |
hoursPerSchoolDay | NUMERIC | Optional | Planned hours per instructional day. When present, Analytics returns how many school days are needed and the date reached at that pace. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | analytics.school_days_remaining. | dictionary |
student_id | TEXT | required | Canonical Alpha student id used for the calendar/enrollment lookup. | dictionary |
school_id | TEXT | required | School id whose alpha.school_calendar rows were read. | dictionary |
as_of | DATE | required | Inclusive start date for the forward count. | dictionary |
end_date | DATE | required | Exclusive end date for the forward count. | dictionary |
school_day_policy_ref | TEXT | required | Policy ref used to decide which calendar dates count as instructional days and which MAP days are excluded. | dictionary |
instructional_days_remaining | INTEGER | required | Count of school days after applying alpha.policy.school_day over the half-open [asOf, endDate) window. | dictionary |
calendar_ref_count | INTEGER | required | Count of calendar rows inspected before filtering to instructional days. | dictionary |
first_instructional_date | DATE nullable | required | First remaining instructional day, or null when no school day remains. | dictionary |
last_instructional_date | DATE nullable | required | Last remaining instructional day in the requested window. | dictionary |
instructional_dates | DATE[] | required | Instructional dates in the requested window. Render or cache these only as the surface-returned answer; do not recreate them from a private calendar. | dictionary |
xp_unit.policy_ref | TEXT | required | Versioned policy that defines XP-to-expected-minute conversion. | dictionary |
xp_unit.xp_per_expected_minute | NUMERIC | required | Surface-owned unit convention: 1 XP equals 1 expected minute. | dictionary |
xp_unit.xp_per_hour | NUMERIC | required | Surface-owned conversion: 60 XP equals one expected hour. | dictionary |
xp_unit.meaning | TEXT | required | Plain-language statement of the XP time conversion. | dictionary |
effort.xp_remaining | NUMERIC nullable | required | Caller-supplied remaining XP when xpRemaining was used. | dictionary |
effort.expected_minutes_remaining | NUMERIC nullable | required | Remaining effort in expected minutes after applying the surface XP/minutes/hours conversion. | dictionary |
effort.hours_remaining | NUMERIC nullable | required | Remaining effort in hours. | dictionary |
effort.target_date | DATE nullable | required | Requested target date, if supplied. | dictionary |
effort.school_days_until_target_date | INTEGER nullable | required | Instructional days between asOf and targetDate. | dictionary |
effort.required_hours_per_school_day_to_target_date | NUMERIC nullable | required | Hours per remaining school day needed to finish by targetDate. | dictionary |
effort.hours_per_school_day | NUMERIC nullable | required | Caller-supplied pace assumption. | dictionary |
effort.school_days_needed_at_hours_per_school_day | INTEGER nullable | required | Instructional days needed at the supplied pace. | dictionary |
effort.target_date_at_hours_per_school_day | DATE nullable | required | Instructional date reached at the supplied pace, or null when the window is too short. | dictionary |
effort.can_finish_by_end_date_at_hours_per_school_day | BOOLEAN nullable | required | Whether the supplied pace finishes within the requested forward window. | dictionary |
provenance.source | TEXT | required | Calendar source read by Analytics. | dictionary |
provenance.source_ref_sample | TEXT[] | required | Sample Ed-Fi CalendarDate-derived refs used for audit. | dictionary |
provenance.policy_ref | TEXT | required | Policy ref used for the school-day decision. | dictionary |
provenance.xp_unit_policy_ref | TEXT | required | XP expected-minute policy ref used for effort projection. | dictionary |
links.self | URL | required | The request URL for this read. | dictionary |
links.schoolDayMinutes | URL | required | Related school-day-minutes rollup endpoint. | dictionary |
links.xpRollups | URL | required | Related XP rollup endpoint. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/school-days-remaining?studentId=student-ada-001&asOf=2026-06-14&endDate=2026-07-24&xpRemaining=600&hoursPerSchoolDay=1.5&targetDate=2026-07-24" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/school-days-remaining`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "analytics.school_days_remaining",
"student_id": "student-ada-001",
"school_id": "school_alpha_demo",
"as_of": "2026-06-14",
"end_date": "2026-07-24",
"school_day_policy_ref": "alpha.policy.school_day.v2026-06-10",
"instructional_days_remaining": 29,
"calendar_ref_count": 40,
"first_instructional_date": "2026-06-15",
"last_instructional_date": "2026-07-23",
"instructional_dates": [
"2026-06-15",
"2026-06-16",
"2026-06-17"
],
"xp_unit": {
"policy_ref": "alpha.policy.analytics.xp_expected_minute.v2026-06-14",
"xp_per_expected_minute": 1,
"xp_per_hour": 60,
"meaning": "1 XP = 1 expected minute; XP hours = XP / 60."
},
"effort": {
"xp_remaining": 600,
"expected_minutes_remaining": 600,
"hours_remaining": 10,
"target_date": "2026-07-24",
"school_days_until_target_date": 29,
"required_hours_per_school_day_to_target_date": 0.345,
"hours_per_school_day": 1.5,
"school_days_needed_at_hours_per_school_day": 7,
"target_date_at_hours_per_school_day": "2026-06-23",
"can_finish_by_end_date_at_hours_per_school_day": true
},
"provenance": {
"source": "alpha.school_calendar",
"source_ref_sample": [
"ed_fi.CalendarDate:school_alpha_demo:2026-06-15"
],
"policy_ref": "alpha.policy.school_day.v2026-06-10",
"xp_unit_policy_ref": "alpha.policy.analytics.xp_expected_minute.v2026-06-14"
},
"links": {
"self": "/alpha/analytics/v1/school-days-remaining?studentId=student-ada-001&asOf=2026-06-14&endDate=2026-07-24",
"schoolDayMinutes": "/alpha/analytics/v1/school-day-minutes?studentId=student-ada-001",
"xpRollups": "/alpha/analytics/v1/xp-rollups?studentId=student-ada-001&windowKind=school_year"
}
}
GET
Read working and mastered age-grade status
/alpha/analytics/v1/grade-level-status
Return the two named GOALS age-grade comparisons. Analytics reads P&O age grade and Results grade positions, then returns working-vs-age and mastered-vs-age labels so clients do not recompute or rename ahead/at/behind.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Required | Canonical Alpha student id. |
subject | subject_id enum | Required | Canonical subject id. subjectId is also accepted by the runtime. |
asOfDate | DATE | Required | Point-in-time date for P&O age grade and Results grade state. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
object | TEXT | required | analytics.grade_level_status. | dictionary |
student_id | TEXT | required | Canonical Alpha student id. | dictionary |
subject_id | TEXT | required | Canonical subject id used for Results working/HMG lookup. | dictionary |
as_of_date | DATE | required | Point-in-time date for P&O age grade and Results state. | dictionary |
age_grade | INTEGER nullable | required | School-assigned cohort grade from People & Orgs alpha.age_grade_history. | dictionary |
working_grade | INTEGER nullable | required | Results-owned working grade. | dictionary |
highest_mastered_grade | INTEGER nullable | required | Results-owned highest mastered grade. | dictionary |
working_age_grade_delta | INTEGER nullable | required | working_grade - age_grade. | dictionary |
working_age_grade_status | TEXT enum | required | working_behind_age_grade, working_at_age_grade, working_above_age_grade, or unknown. | dictionary |
mastered_age_grade_delta | INTEGER nullable | required | highest_mastered_grade - age_grade. | dictionary |
mastered_age_grade_status | TEXT enum | required | mastered_behind_age_grade, mastered_at_age_grade, mastered_above_age_grade, or unknown. | dictionary |
source_refs | JSON object | required | Owner pointers for P&O age grade and Results grade positions. | dictionary |
deprecated_aliases | JSON object | required | Compatibility mapping only; not the preferred public contract. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/grade-level-status?studentId=b6fa7128-f641-4efd-9075-375411fd6c39&subject=math&asOfDate=2026-05-20" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/grade-level-status`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "analytics.grade_level_status",
"student_id": "b6fa7128-f641-4efd-9075-375411fd6c39",
"subject_id": "math",
"as_of_date": "2026-05-20",
"age_grade": 5,
"working_grade": 6,
"highest_mastered_grade": 5,
"working_age_grade_delta": 1,
"working_age_grade_status": "working_above_age_grade",
"mastered_age_grade_delta": 0,
"mastered_age_grade_status": "mastered_at_age_grade",
"source_refs": {
"age_grade": "people_and_orgs.alpha.age_grade_history",
"working_grade": "results.student_track_state.working_grade",
"highest_mastered_grade": "results.highest_mastered_grade"
},
"deprecated_aliases": {
"instructional_level_status": "working_age_grade_status",
"strict_mastery_status": "mastered_age_grade_status"
}
}
GET
List completion rollups
/alpha/analytics/v1/completion-rollups
Read progress evidence used by Results and audit views. Course rows preserve app-reported Caliper percent and XP remaining evidence; student-facing course and grade-level progress comes from Results course-progress and grade-level-progress.
Request and query parameters
| Name | Type | Required | Description |
|---|
studentId | TEXT | Optional | Canonical Alpha student id. |
completionScope | completion_scope enum | Optional | course, subject, track, track_level, grade_level, or segment. |
scopeId | TEXT | Optional | Course, subject, track, track-level, grade-level, or segment scope id. Grade-level scope ids are grade_level:{subject}:{grade}. |
subjectId | subject_id enum | Optional | Canonical Alpha subject id. |
startDate | DATE | Optional | Inclusive reporting window start. |
endDate | DATE | Optional | Exclusive reporting window end. |
modifiedSince | TIMESTAMPTZ | Optional | Poll for changed rows. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
id | UUID | required | Analytics-owned stable row id for this derived fact. Generated by the platform; globally unique within alpha.analytics_* objects. | dictionary |
student_id | TEXT | required | Canonical Alpha student id the source Event or Result resolved to at materialization time. Must resolve through People and Orgs as a real student for the source timestamp/effective date. | dictionary |
completion_scope | TEXT enum | required | Curriculum/reporting scope for this completion measure. Allowed values: completion_scope enum. | dictionary |
scope_id | TEXT | required | Identifier of the Curriculum or subject scope being measured. Must match completion_scope: course id for course, subject id for subject, track id for track, etc. | dictionary |
subject_id | TEXT | nullable; required when completion_scope=subject or the scope resolves to one subject | Canonical Alpha subject used for reporting and grouping. Closed Alpha subject enum after write-time alias folds; unknown source subject becomes an adapter finding or null_reason, not a new string. | dictionary |
grade_level | INTEGER | nullable for non-grade-level scopes without a course grade | Working grade level associated with the course progress row, or the grade represented by a grade_level aggregate. Must come from the course/Caliper/Curriculum scope metadata, not from MAP/RIT/R90. | dictionary |
is_main_course | BOOLEAN | nullable when not applicable; true for main course and grade_level aggregate rows | Whether the course row counts toward grade-level progress. Main grade-level courses count; remediation, hole-filling, catalog, practice, and review courses do not. | dictionary |
progress_source_kind | TEXT enum | required for reportable progress rows | Which evidence source produced completion_percent. Allowed values include app_reported_percent, xp_remaining, lesson_count, subject_aggregate, and main_course_aggregate. | dictionary |
xp_earned | NUMERIC(14,3) | nullable when progress_source_kind is app_reported_percent or an aggregate without XP evidence | Earned XP for this completion scope when the row uses the XP fallback. Nonnegative when present; source corrections are reflected through quality_status/corrections before this value is reportable. | dictionary |
xp_remaining | NUMERIC(14,3) | nullable when progress_source_kind is app_reported_percent or an aggregate without XP evidence | Remaining expected XP for this completion scope when the row uses the XP fallback. Nonnegative when present; computed from Curriculum expected XP refs under denominator_policy_ref. | dictionary |
completion_percent | NUMERIC(7,3) | nullable when no direct percent, XP denominator, lesson denominator, or source/policy is available | Report-ready completion measure for the scope. Direct app percent wins. If absent, fallback formula is xp_earned / (xp_earned + xp_remaining) * 100. Normally 0 to 100; null requires null_reason. | dictionary |
denominator_policy_ref | TEXT | required | Policy that decides which Curriculum expected-XP refs belong in the remaining-XP denominator. Named versioned progress policy, for example alpha.policy.analytics.course_progress.v2026-06-15 or alpha.policy.curriculum.main_course_grade_level.v2026-06-15, or inherited Curriculum policy. | dictionary |
curriculum_scope_refs | JSONB | required | Compact ids/cursor for Curriculum refs used to form the denominator. Refs/hashes only; never copies Curriculum trees or Content rows. | dictionary |
source_import_id | TEXT | required for report-tile rows; nullable for purely derived event-axis rows | Analytics source-import receipt that proves which named report-source adapter produced this report-grade fact. Must reference alpha.analytics_source_import for non-null Learning Report tile values; no literal or fixture value may be reportable without this provenance. | dictionary |
null_reason | TEXT enum | required | Why completion_percent is null or not reportable. Allowed values: null_reason enum; none means the metric is populated. | dictionary |
quality_status | TEXT enum | required | Current materialization quality state for this fact. Allowed values: quality_status enum. Ordinary report reads keep ok, corrected, and signed reversed rows according to the table rule; audit reads may include source_missing/source_unlinked/policy_pending/adapter_rejected. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/completion-rollups?studentId=student_01HT7G3YZV7QB5N4YKQ1K0Z9A9&completionScope=course&scopeId=course_math_grade_4_powerpath" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/completion-rollups`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/completion-rollups",
"data": [
{
"id": "6d7d3b01-6b70-44ce-90dd-e7cf2f690006",
"student_id": "student_01HT7G3YZV7QB5N4YKQ1K0Z9A9",
"completion_scope": "course",
"scope_id": "course_math_grade_4_powerpath",
"subject_id": "math",
"grade_level": 4,
"is_main_course": true,
"progress_source_kind": "app_reported_percent",
"xp_earned": null,
"xp_remaining": null,
"completion_percent": 75,
"denominator_policy_ref": "alpha.policy.analytics.course_progress.v2026-06-15",
"curriculum_scope_refs": [
"course_component_01HT8ROOT",
"progress_source:app_reported_percent",
"course_role:main",
"grade_level:4"
],
"source_import_id": "imp_01HT8COMPLETION_2026_SPRING",
"null_reason": "none",
"quality_status": "ok",
"modified_at": "2026-06-01T01:03:00Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/completion-rollups"
}
}
POST
Import TimeBack XP, time, and accuracy facts
/alpha/analytics/v1/source-imports/timeback-xp-time-accuracy
Submit source-shaped reporting.processed_facts rows. Analytics normalizes subject, time, XP, and source refs server-side.
Request and query parameters
| Name | Type | Required | Description |
|---|
Idempotency-Key | HTTP header | Required | Required for source imports. Same key plus different body returns analytics:idempotency_conflict. |
sourceBatchRef | TEXT | Required | Operator-controlled batch reference for audit and reconciliation. |
rows | JSON array | Required | Raw source-shaped processed_facts rows. Do not pre-normalize subject, time, XP, or student identity. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
import_id | TEXT | required | Stable id for this source import request. Generated by platform; unique per tenant. | dictionary |
status | TEXT enum | required | Processing state of the source import. Allowed values: import_status enum. | dictionary |
submitted_row_count | INTEGER | required | Number of source-shaped rows submitted in the batch. Nonnegative. | dictionary |
accepted_row_count | INTEGER | required | Rows accepted by request validation and adapter mapping. Nonnegative and not greater than submitted_row_count. | dictionary |
materialized_row_count | INTEGER | required | Rows that produced readable Analytics facts. Nonnegative; successful nonempty imports must produce readable rows unless status explains otherwise. | dictionary |
rejected_row_count | INTEGER | required | Rows rejected by validation or adapter mapping. Nonnegative; submitted = accepted + rejected for completed imports. | dictionary |
problem_counts | JSONB | required | Counts of typed Problem codes produced by the import. Keys must be stable analytics:* codes; values nonnegative integers. | dictionary |
completed_at | TIMESTAMPTZ | nullable until processing completes | When import processing reached a terminal status. UTC timestamp; null while status is received/running. | dictionary |
cURL
curl -sS -X POST "$ANALYTICS_BASE_URL/alpha/analytics/v1/source-imports/timeback-xp-time-accuracy" \
-H "Authorization: Bearer $ANALYTICS_TOKEN" \
-H "Idempotency-Key: pfacts-demo-2026-05-01" \
-H "Content-Type: application/json" \
--data '{"sourceBatchRef":"migration-demo-2026-05","rows":[{"date":"2026-05-14","subject":"Math","app":"Math Academy","active_seconds":420,"inactive_seconds":0,"waste_seconds":0,"xp_earned":18}]}'
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/source-imports/timeback-xp-time-accuracy`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Idempotency-Key": "demo-import-001", "Content-Type": "application/json" }, body: JSON.stringify({ sourceBatchRef: "demo", rows: [] }) });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"import_id": "import_01J0ANALYTICS",
"status": "materialized",
"submitted_row_count": 250,
"accepted_row_count": 249,
"materialized_row_count": 249,
"rejected_row_count": 1,
"problem_counts": {
"analytics:source_unlinked": 1
},
"completed_at": "2026-06-12T17:05:00Z"
}
POST
Import TimeBack MAP results
/alpha/analytics/v1/source-imports/timeback-map
Submit source-shaped reporting.hp_map_results rows. Analytics normalizes student, subject, term, norms, test-of-record, Growth X, and R90 position server-side.
Request and query parameters
| Name | Type | Required | Description |
|---|
Idempotency-Key | HTTP header | Required | Required for source imports. Same key plus different body returns analytics:idempotency_conflict. |
sourceBatchRef | TEXT | Required | Operator-controlled batch reference for audit and reconciliation. |
rows | JSON array | Required | Full source-shaped reporting.hp_map_results rows. Submit the row as exported; do not pre-dedupe, parse terms, normalize subjects, compute Growth X, or apply norms/R90 locally. |
rows[].studentid | TEXT | Required | TimeBack/NWEA student id from hp_map_results. student_sourced_id is accepted only for migration compatibility; the adapter resolves the canonical Alpha student id. |
rows[].course or rows[].subject | TEXT | Required | Source MAP course/subject label such as Math K-12. The adapter writes the canonical subject_id. |
rows[].termname | TEXT | Required | Source MAP term label such as Winter 2025-2026. The adapter writes canonical_term_id; callers do not parse term strings. |
rows[].testritscore | INTEGER | Required | MAP RIT score. Missing RIT returns analytics:adapter_rejected. |
rows[].normsreferencedata | INTEGER enum | Required | NWEA norms family, normally 2020 or 2025. The adapter writes norms_set. |
rows[].wintertowinterobservedgrowth | NUMERIC | Required for winter_to_winter | Observed growth for the Learning Report MAP window. |
rows[].wintertowinterprojectedgrowth | NUMERIC | Required for winter_to_winter | Projected-growth denominator for Growth X. The adapter never uses typical growth as the Learning Report denominator when projected growth is present. |
rows[].testpercentile | INTEGER | Optional | Achievement percentile from hp_map_results. If omitted, Analytics derives it from the surface-owned norms table and stamps the calculator/table version. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
import_id | TEXT | required | Stable id for this source import request. Generated by platform; unique per tenant. | dictionary |
import_kind | TEXT enum | required | Which source/replay contract this batch follows. Allowed values: import_kind enum. | dictionary |
adapter_name | TEXT | required | Named server-side adapter that normalized this source batch. Must match one approved adapter: timeback-xp-time-accuracy, timeback-map, timeback-grade-mastery, or timeback-completion, plus internal replay kinds where applicable. | dictionary |
source_system | TEXT | required | Producer system the adapter read from. Closed governed values for approved adapters: reporting, events, results, migration_reconcile. | dictionary |
source_table | TEXT | required for report-source adapters | Report-source table or producer collection read by the adapter. For the four public report adapters: processed_facts, hp_map_results, assessment_results, or processed_facts+courses+course_components. For timeback-map, source_table=hp_map_results means the adapter accepts the full source-shaped row and ignores unknown extra columns. | dictionary |
status | TEXT enum | required | Processing state of the source import. Allowed values: import_status enum. | dictionary |
submitted_row_count | INTEGER | required | Number of source-shaped rows submitted in the batch. Nonnegative. | dictionary |
accepted_row_count | INTEGER | required | Rows accepted by request validation and adapter mapping. Nonnegative and not greater than submitted_row_count. | dictionary |
materialized_row_count | INTEGER | required | Rows that produced readable Analytics facts. Nonnegative; successful nonempty imports must produce readable rows unless status explains otherwise. | dictionary |
rejected_row_count | INTEGER | required | Rows rejected by validation or adapter mapping. Nonnegative; submitted = accepted + rejected for completed imports. | dictionary |
problem_counts | JSONB | required | Counts of typed Problem codes produced by the import. Keys must be stable analytics:* codes; values nonnegative integers. | dictionary |
completed_at | TIMESTAMPTZ | nullable until processing completes | When import processing reached a terminal status. UTC timestamp; null while status is received/running. | dictionary |
cURL
curl -sS -X POST "$ANALYTICS_BASE_URL/alpha/analytics/v1/source-imports/timeback-map" \
-H "Authorization: Bearer $ANALYTICS_TOKEN" \
-H "Idempotency-Key: hp-map-demo-2026-winter" \
-H "Content-Type: application/json" \
--data '{"sourceBatchRef":"migration-demo-map-2026-winter","rows":[{"id":"hp-map-demo-001","studentid":"student_01HT7G3YZV7QB5N4YKQ1K0Z9A9","course":"Math K-12","termname":"Winter 2025-2026","teststartdate":"2026-01-28","testritscore":239,"testpercentile":91,"wintertowinterconditionalgrowthpercentile":43,"wintertowinterobservedgrowth":6,"wintertowinterprojectedgrowth":7,"typicalwintertowintergrowth":2,"normsreferencedata":2025,"growthmeasureyn":"true","goal1name":"Operations and Algebraic Thinking","goal1ritscore":241}]}'
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/source-imports/timeback-map`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Idempotency-Key": "demo-import-001", "Content-Type": "application/json" }, body: JSON.stringify({ sourceBatchRef: "demo", rows: [] }) });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"import_id": "import_01J0MAPRESULTS",
"import_kind": "timeback-map",
"adapter_name": "timeback-map",
"source_system": "reporting",
"source_table": "hp_map_results",
"status": "materialized",
"submitted_row_count": 12,
"accepted_row_count": 12,
"materialized_row_count": 12,
"rejected_row_count": 0,
"problem_counts": {},
"completed_at": "2026-06-12T17:08:00Z"
}
/alpha/analytics/v1/source-imports
Inspect source-import status, materialized row counts, and typed problem counts. This is the audit trail for migration and backfill.
Request and query parameters
| Name | Type | Required | Description |
|---|
importKind | import_kind enum | Optional | Adapter/import kind, such as timeback-xp-time-accuracy or timeback-map. |
status | import_status enum | Optional | received, running, materialized, rejected, or another governed import status. |
modifiedSince | TIMESTAMPTZ | Optional | Poll for import status changes. |
limit | INTEGER | Optional | Page size. Default 100; maximum 1000. |
cursor | TEXT | Optional | Opaque next-page cursor. |
Response fields
| Field | Type | Nullability | Meaning | Source |
|---|
import_id | TEXT | required | Stable id for this source import request. Generated by platform; unique per tenant. | dictionary |
import_kind | TEXT enum | required | Which source/replay contract this batch follows. Allowed values: import_kind enum. | dictionary |
status | TEXT enum | required | Processing state of the source import. Allowed values: import_status enum. | dictionary |
source_batch_ref | TEXT | nullable | Operator/source identifier for the imported file, replay, or reconciliation run. Opaque reference; no PII or raw row contents. | dictionary |
submitted_row_count | INTEGER | required | Number of source-shaped rows submitted in the batch. Nonnegative. | dictionary |
accepted_row_count | INTEGER | required | Rows accepted by request validation and adapter mapping. Nonnegative and not greater than submitted_row_count. | dictionary |
materialized_row_count | INTEGER | required | Rows that produced readable Analytics facts. Nonnegative; successful nonempty imports must produce readable rows unless status explains otherwise. | dictionary |
rejected_row_count | INTEGER | required | Rows rejected by validation or adapter mapping. Nonnegative; submitted = accepted + rejected for completed imports. | dictionary |
problem_counts | JSONB | required | Counts of typed Problem codes produced by the import. Keys must be stable analytics:* codes; values nonnegative integers. | dictionary |
modified_at | TIMESTAMPTZ | required | Last time the Analytics row changed for polling and modifiedSince queries. UTC timestamp; list endpoints support modifiedSince against this field. | dictionary |
cURL
curl -sS "$ANALYTICS_BASE_URL/alpha/analytics/v1/source-imports?importKind=timeback-map&modifiedSince=2026-06-10T00:00:00Z" \
-H "Authorization: Bearer $ANALYTICS_TOKEN"
JavaScript
const response = await fetch(`${base}/alpha/analytics/v1/source-imports`, { headers: { Authorization: `Bearer ${token}` } });
if (!response.ok) throw await response.json();
const body = await response.json();
Example response
{
"object": "list",
"url": "/alpha/analytics/v1/source-imports",
"data": [
{
"import_id": "imp_01HT8ANALYTICS",
"import_kind": "timeback-xp-time-accuracy",
"status": "materialized",
"source_batch_ref": "migration-2026-06-10-reporting-processed-facts-001",
"submitted_row_count": 5000,
"accepted_row_count": 4970,
"materialized_row_count": 4970,
"rejected_row_count": 30,
"problem_counts": {
"analytics:adapter_rejected": 30
},
"modified_at": "2026-06-10T00:56:12Z"
}
],
"hasMore": false,
"nextCursor": null,
"links": {
"self": "/alpha/analytics/v1/source-imports"
}
}