Progress Reporting
Supported Endpoints
| Endpoint | Permission | Purpose |
|---|---|---|
GET /api/v1/progress/events |
read_progress |
Poll durable progress events in cursor order |
GET /api/v1/progress |
read_progress |
Get one learner's aggregate progress in one course |
GET /api/v1/progress/summary |
read_progress |
Get one learner's progress across courses with activity |
GET /api/v1/courses/{course_id}/progress |
read_progress |
Get roster-level progress for learners with activity in a course |
GET /api/v1/progress/courses/{course_id} |
read_progress |
Get one learner's per-topic progress in one course |
Poll Progress Events
GET /api/v1/progress/events returns McCoy Surface session lifecycle events in feed order.
curl "https://api.mccoy.org/api/v1/progress/events?course_id=550e8400-e29b-41d4-a716-446655440000&page_size=100" \
-H "X-Api-Key: YOUR_API_KEY"
Filters:
| Parameter | Description |
|---|---|
learner_id |
Optional McCoy learner ID |
course_id |
Optional course UUID |
event_type |
Optional comma-separated event types |
start_date |
Optional inclusive activity date, YYYY-MM-DD |
end_date |
Optional inclusive activity date, YYYY-MM-DD |
page_size |
Results per page, default 100, max 500 |
cursor |
Opaque cursor from the previous response |
Process events in response order and dedupe by event_id. Store pagination.next_cursor after every successful page, including empty pages.
Event source is surface. Event types can include session_started, session_completed, and session_abandoned.
{
"data": {
"events": [
{
"event_id": "event-uuid",
"event_type": "session_completed",
"event_timestamp": "2026-06-02T14:22:00Z",
"emitted_at": "2026-06-02T14:22:01Z",
"source": "surface",
"learner_id": "lrn_01HX7N8V2K9Q4R6M3B5C7D9E0F",
"course_id": "550e8400-e29b-41d4-a716-446655440000",
"course_name": "Biology 101",
"session_id": "session-uuid",
"level_id": "level-uuid",
"level_name": "Level 1",
"session_type": "learn",
"score_correct": 8,
"score_total": 10,
"score_percentage": 80.0
}
]
},
"pagination": {
"page_size": 100,
"next_cursor": "opaque-cursor",
"has_more": false
}
}
Read Learner Progress
GET /api/v1/progress returns one learner's aggregate progress in one course.
curl "https://api.mccoy.org/api/v1/progress?course_id=550e8400-e29b-41d4-a716-446655440000&learner_id=lrn_01HX7N8V2K9Q4R6M3B5C7D9E0F" \
-H "X-Api-Key: YOUR_API_KEY"
Single-course progress can return not_started, in_progress, or completed.
{
"data": {
"learner_id": "lrn_01HX7N8V2K9Q4R6M3B5C7D9E0F",
"course_id": "550e8400-e29b-41d4-a716-446655440000",
"course_name": "Biology 101",
"completion_percentage": 45.0,
"status": "in_progress",
"levels_completed": 9,
"total_levels": 20,
"first_activity_at": "2026-06-01T08:30:00Z",
"last_activity_at": "2026-06-02T14:22:00Z"
}
}
GET /api/v1/progress/summary lists courses where the learner has activity.
Parameters:
| Parameter | Description |
|---|---|
learner_id |
Required McCoy learner ID |
status |
Optional comma-separated filter: completed, in_progress |
sort_by |
Optional: completion_percentage, last_activity_at, first_activity_at, course_name, levels_completed |
sort_order |
Optional: asc or desc; default desc |
curl "https://api.mccoy.org/api/v1/progress/summary?learner_id=lrn_01HX7N8V2K9Q4R6M3B5C7D9E0F" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": {
"learner_id": "lrn_01HX7N8V2K9Q4R6M3B5C7D9E0F",
"summary": {
"total_courses": 2,
"completed": 1,
"in_progress": 1,
"total_levels_completed": 14
},
"courses": [
{
"course_id": "550e8400-e29b-41d4-a716-446655440000",
"course_name": "Biology 101",
"completion_percentage": 100.0,
"status": "completed",
"levels_completed": 10,
"total_levels": 10,
"first_activity_at": "2026-06-01T08:30:00Z",
"last_activity_at": "2026-06-10T16:45:00Z"
},
{
"course_id": "660e8400-e29b-41d4-a716-446655440001",
"course_name": "Chemistry 101",
"completion_percentage": 40.0,
"status": "in_progress",
"levels_completed": 4,
"total_levels": 10,
"first_activity_at": "2026-06-12T09:15:00Z",
"last_activity_at": "2026-06-18T11:20:00Z"
}
]
}
}
Read Course Progress
GET /api/v1/courses/{course_id}/progress returns progress for learners with activity in a course. Use it for instructor, admin, or reporting dashboards.
Parameters:
| Parameter | Description |
|---|---|
page |
Optional page number, default 1 |
page_size |
Optional results per page, default 50, max 100 |
status |
Optional comma-separated filter: completed, in_progress |
sort_by |
Optional: completion_percentage, last_activity_at, first_activity_at, levels_completed |
sort_order |
Optional: asc or desc; default desc |
inactive_days |
Optional filter to learners with no activity for at least this many days |
GET /api/v1/progress/courses/{course_id}?learner_id=... returns the same learner scoped to per-topic completion counts for one course.
{
"data": {
"course_id": "550e8400-e29b-41d4-a716-446655440000",
"last_active_topic_id": "770e8400-e29b-41d4-a716-446655440002",
"last_activity_at": "2026-06-02T14:22:00Z",
"topics": [
{
"topic_id": "770e8400-e29b-41d4-a716-446655440002",
"completed_count": 8,
"total_count": 10,
"attempted_count": 9,
"accuracy": 0.89
}
],
"total_completed": 8,
"total_attempted": 9,
"accuracy": 0.89,
"percentage": 80.0
}
}
Error Codes
| HTTP Status | Code | Description |
|---|---|---|
| 400 | MISSING_FIELDS |
Required field missing |
| 400 | INVALID_REQUEST |
Malformed request or invalid field value |
| 400 | INVALID_PARAMETERS |
Invalid feed filter, pagination value, or cursor |
| 401 | UNAUTHORIZED |
Missing or invalid API key |
| 403 | COURSE_ACCESS_DENIED |
No access to the specified course |
| 404 | LEARNER_NOT_FOUND |
The learner_id was not found for this API key |
| 404 | RESOURCE_NOT_FOUND |
Course not found or unavailable for this API key |