REST API Reference
LearnDash Dashboard exposes two REST API namespaces:
ldd_report/v1— User statisticsld-dashboard/v2— Reports, charts, and private messaging
Base URL: {site_url}/wp-json/
Authentication
Section titled “Authentication”All endpoints require a logged-in WordPress session. Use cookie-based authentication with the X-WP-Nonce header.
// Get nonce from localized dataconst nonce = ldReportData.nonce; // or ld_dashboard_public_js.nonce
fetch( '/wp-json/ld-dashboard/v2/reports', { headers: { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json', }, credentials: 'same-origin',} );The nonce is generated with wp_create_nonce( 'wp_rest' ).
Messaging Endpoints
Section titled “Messaging Endpoints”Namespace: ld-dashboard/v2
Messaging requires the enable-private-messaging setting to be enabled. All messaging endpoints require the user to be logged in.
GET /messages
Section titled “GET /messages”Retrieve conversations (inbox) for the current user.
Permission: Logged in
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int | 1 | Page number |
per_page |
int | 20 | Conversations per page (max 50) |
Response:
{ "conversations": [ { "thread_id": 42, "subject": "Question about Lesson 3", "course_id": 100, "course_name": "Introduction to PHP", "other_user": { "id": 5, "name": "Jane Smith", "avatar": "https://example.com/avatar.jpg" }, "last_message": "Thanks for the clarification!", "last_message_at": "2026-02-20 14:32:00", "unread_count": 2 } ], "total": 15, "pages": 1}POST /messages
Section titled “POST /messages”Send a new message or reply to an existing thread.
Permission: Logged in
Body parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
recipient_id |
int | Yes | WordPress user ID of the recipient |
course_id |
int | Yes | Course ID for context (required for new threads) |
subject |
string | Yes* | Subject line (*required for new threads only) |
message |
string | Yes | Message body (HTML allowed via wp_kses_post) |
parent_id |
int | No | Thread ID to reply to (omit for new conversations) |
Messaging rules:
- Admins can message anyone.
- Instructors can message students enrolled in their courses and co-instructors.
- Group leaders can message members of their groups.
- Students can only message instructors of their enrolled courses.
Response (201):
{ "id": 57, "message": "Message sent successfully.", "thread_id": 42}Error (403):
{ "code": "rest_forbidden", "message": "You cannot message this user. You must share a course relationship.", "data": { "status": 403 }}GET /messages/{id}
Section titled “GET /messages/{id}”Get all messages in a thread.
Permission: Must be the sender or recipient of the thread
URL parameters:
| Parameter | Type | Description |
|---|---|---|
id |
int | Thread ID (root message ID) |
Response:
{ "thread_id": 42, "subject": "Question about Lesson 3", "course_id": 100, "course_name": "Introduction to PHP", "other_user": { "id": 5, "name": "Jane Smith", "avatar": "https://example.com/avatar.jpg" }, "messages": [ { "id": 42, "sender_id": 5, "sender": { "id": 5, "name": "Jane Smith", "avatar": "https://example.com/avatar.jpg" }, "message": "<p>Hi, I have a question...</p>", "read_at": null, "created_at": "2026-02-20 12:00:00", "is_mine": false } ]}DELETE /messages/{id}
Section titled “DELETE /messages/{id}”Soft-delete a thread for the current user.
Permission: Must be the sender or recipient
Deletion is user-specific: the other participant still sees the conversation. The row is permanently deleted only when both users have deleted it.
Response (200):
{ "deleted": true}PUT /messages/{id}/read
Section titled “PUT /messages/{id}/read”Mark all messages in a thread as read for the current user.
Permission: Must be the sender or recipient
Response (200):
{ "success": true}GET /messages/unread-count
Section titled “GET /messages/unread-count”Get the total unread message count for the current user.
Permission: Logged in
Response:
{ "count": 3}GET /messages/recipients
Section titled “GET /messages/recipients”Get eligible message recipients for the current user.
Permission: Logged in
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
course_id |
int | 0 | Filter recipients by course context |
search |
string | "" |
Search by display name, login, or email |
Response:
[ { "id": 12, "name": "John Doe", "email": "john@example.com", "avatar": "https://example.com/avatar.jpg", "role": "Instructor" }]Report Endpoints
Section titled “Report Endpoints”Namespace: ld-dashboard/v2
Report endpoints require the user to pass LD_Dashboard_REST_Permissions::check(). Administrators, instructors, and group leaders have access. Students are automatically scoped to their own data.
GET /reports
Section titled “GET /reports”List all reports and charts available to the current user.
Permission: Administrator, instructor, or group leader
Response:
{ "success": true, "data": { "tables": { "quiz-results": { "id": "quiz-results", "title": "Quiz Results", "type": "table", "exports": ["csv", "excel"] } }, "charts": { "course-completion": { "id": "course-completion", "title": "Course Completion", "type": "chart", "chartType": "doughnut" } } }}GET /reports/{report_id}
Section titled “GET /reports/{report_id}”Get data for a specific table report.
Also accepts POST (for large filter payloads).
Permission: Administrator, instructor, or group leader
URL parameters:
| Parameter | Type | Description |
|---|---|---|
report_id |
string | Report ID (e.g., quiz-results, essay-submissions) |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
course_id |
int | 0 | Filter by course ID |
group_id |
int | 0 | Filter by group ID |
lesson_id |
int | 0 | Filter by lesson ID |
user_id |
int | 0 | Filter by user ID (auto-set for students) |
status |
string | all |
Filter by status |
per_page |
int | -1 | Items per page (-1 for all) |
page |
int | 1 | Page number |
Response:
{ "success": true, "data": { "id": "quiz-results", "title": "Quiz Results", "type": "table", "columns": [ { "data": "student_name", "title": "Student", "visible": true, "orderable": true } ], "data": [ /* row objects */ ], "exports": ["csv", "excel"], "meta": { "report_id": "quiz-results", "report_type": "table", "total": 42, "cached_at": "2026-02-20 14:00:00" } }}Report data is cached in WordPress transients for 1 hour (by default per get_cache_duration()).
DELETE /reports/{report_id}/cache
Section titled “DELETE /reports/{report_id}/cache”Clear cached data for a specific report.
Permission: Administrator only
Response:
{ "success": true, "message": "Cache cleared successfully."}Chart data via reports endpoint
Section titled “Chart data via reports endpoint”Charts are served through the same /reports/{id} endpoint. The response structure differs for chart types.
Example chart response:
{ "success": true, "data": { "id": "course-completion", "title": "Course Completion", "type": "chart", "chartType": "doughnut", "chartData": { "labels": ["Completed", "In Progress", "Not Started"], "datasets": [ { "label": "Course Completion", "data": [45, 30, 25], "backgroundColor": ["rgba(46,204,113,0.8)", "rgba(241,196,15,0.8)", "rgba(113,125,150,0.8)"], "borderColor": ["rgba(46,204,113,1)", "rgba(241,196,15,1)", "rgba(113,125,150,1)"], "borderWidth": 1 } ] }, "options": { "responsive": true, "maintainAspectRatio": true, "plugins": { "legend": { "display": true, "position": "top" } } }, "meta": { "report_id": "course-completion", "report_type": "chart", "chart_type": "doughnut", "total": 3, "cached_at": "2026-02-20 14:00:00" } }}Chart query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
course_id |
int | 0 | Filter by course |
group_id |
int | 0 | Filter by group |
filter |
string | year |
Time range: year, month, or week |
date_from |
string | "" |
Start date in YYYY-MM-DD format |
date_to |
string | "" |
End date in YYYY-MM-DD format |
Available Reports
Section titled “Available Reports”Use these IDs as the report_id parameter in /reports/{report_id} requests.
Table Reports
Section titled “Table Reports”| Report ID | Title | Description |
|---|---|---|
quiz-results |
Quiz Results | Quiz attempt scores and pass/fail status per student |
essay-submissions |
Essay Submissions | Student essay submissions with grading status |
assignment-status |
Assignment Status | Assignment submissions with approval status |
course-progress |
Course Progress | Student progress through enrolled courses |
time-by-course |
Time by Course | Learning time totals broken down by course |
time-by-student |
Time by Student | Learning time totals broken down by student |
time-by-content |
Time by Content | Learning time broken down by lesson or topic |
instructor-performance |
Instructor Performance | Course completion rates and student counts per instructor |
group-performance |
Group Performance | Completion rates and activity per LearnDash group |
quiz-question-analysis |
Quiz Question Analysis | Per-question pass/fail rates across all attempts |
Chart Reports
Section titled “Chart Reports”| Chart ID | Title | Chart Type | Description |
|---|---|---|---|
course-completion |
Course Completion | Doughnut | Completed vs. in-progress vs. not started |
top-courses |
Top Courses | Bar | Courses ranked by enrollment or completion count |
instructor-earnings |
Instructor Earnings | Bar | Commission earnings per instructor over time |
time-spent |
Time Spent | Bar | Total learning time with week/month/year filter |
time-trend |
Time Trend | Line | Learning time trend over a selected period |
enrollment-trends |
Enrollment Trends | Line | New enrollments over time |
revenue-per-course |
Revenue per Course | Bar | Revenue broken down by course |
course-dropoff |
Course Drop-off | Bar | Where students stop progressing in a course |
Registering Custom Reports
Section titled “Registering Custom Reports”Use the ld_dashboard_reports_registered action to register your own report class after built-in reports load:
add_action( 'ld_dashboard_reports_registered', function( $registry ) { LD_Dashboard_Report_Registry::register( 'my-report', 'My_Custom_Report_Class' );} );Your class must extend LD_Dashboard_Report_Base (table) or LD_Dashboard_Chart_Base (chart).
User Statistics Endpoint
Section titled “User Statistics Endpoint”Namespace: ldd_report/v1
GET /user/{id}/statistics
Section titled “GET /user/{id}/statistics”Get aggregate statistics for a specific user.
URL: /wp-json/ldd_report/v1/user/{id}/statistics
Permission: Administrator, instructor, or the user themselves
URL parameters:
| Parameter | Type | Description |
|---|---|---|
id |
int | WordPress user ID |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
force |
bool | false |
Bypass transient cache and recalculate fresh statistics |
Response:
{ "success": true, "cached": true, "data": { "user_id": 5, "course_count": 12, "lessons_count": 48, "topics_count": 96, "quizzes_count": 24, "enrolled_course_count": 3, "active_course_count": 2, "completed_course_count": 1, "approved_assignment_count": 7, "not_approved_assignment_count": 3, "graded_essays_count": 5, "not_graded_essays_count": 2, "students_count": 143, "group_count": 4, "certificate_count": 1, "calculated_at": "2026-02-20 14:00:00" }}cached is true when the response was served from the transient cache. Pass ?force=true to recalculate and refresh the cache. Statistics are cached for 1 hour per user.
Response field reference:
| Field | Description |
|---|---|
course_count |
Courses created (instructors/admins) or completed quizzes (students) |
lessons_count |
Lessons in the user’s courses |
topics_count |
Topics in the user’s courses |
quizzes_count |
Quizzes in the user’s courses (or completed quizzes for students) |
enrolled_course_count |
Total courses the user is enrolled in |
active_course_count |
Enrolled courses with status in_progress |
completed_course_count |
Enrolled courses with status completed |
approved_assignment_count |
Assignments with approval_status = 1 |
not_approved_assignment_count |
Assignments pending approval |
graded_essays_count |
Essays marked as graded |
not_graded_essays_count |
Essays pending grading |
students_count |
Students enrolled in the user’s courses or groups |
group_count |
LearnDash groups the user belongs to or manages |
certificate_count |
Certificates earned by the user |
calculated_at |
MySQL timestamp of when statistics were last calculated |
Error Responses
Section titled “Error Responses”All endpoints return standard WordPress REST error objects on failure:
{ "code": "ld_dashboard_forbidden", "message": "You do not have permission to view this report.", "data": { "status": 403 }}| HTTP Status | Code | Meaning |
|---|---|---|
| 401 | rest_forbidden |
Not logged in |
| 403 | ld_dashboard_forbidden |
Insufficient permissions |
| 404 | ld_dashboard_not_found |
Report or message not found |
| 500 | ld_dashboard_error |
Server error during data fetch |

