REST API Reference

Get Started

REST API Reference

LearnDash Dashboard exposes two REST API namespaces:

  • ldd_report/v1 — User statistics
  • ld-dashboard/v2 — Reports, charts, and private messaging

Base URL: {site_url}/wp-json/


Authentication

All endpoints require a logged-in WordPress session. Use cookie-based authentication with the X-WP-Nonce header.

// Get nonce from localized data
const 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<em>create</em>nonce( 'wp_rest' ).


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

Retrieve conversations (inbox) for the current user.

Permission: Logged in

Parameters:

ParameterTypeDefaultDescription
pageint1Page number
per_pageint20Conversations 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

Send a new message or reply to an existing thread.

Permission: Logged in

Body parameters:

ParameterTypeRequiredDescription
recipient_idintYesWordPress user ID of the recipient
course_idintYesCourse ID for context (required for new threads)
subjectstringYes*Subject line (*required for new threads only)
messagestringYesMessage body (HTML allowed via wp<em>kses</em>post)
parent_idintNoThread 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}

Get all messages in a thread.

Permission: Must be the sender or recipient of the thread

URL parameters:

ParameterTypeDescription
idintThread 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}

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

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

Get the total unread message count for the current user.

Permission: Logged in

Response:

{
    "count": 3
}

GET /messages/recipients

Get eligible message recipients for the current user.

Permission: Logged in

Parameters:

ParameterTypeDefaultDescription
course_idint0Filter recipients by course context
searchstring""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

Namespace: ld-dashboard/v2

Report endpoints require the user to pass LD<em>Dashboard</em>REST_Permissions::check(). Administrators, instructors, and group leaders have access. Students are automatically scoped to their own data.


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}

Get data for a specific table report.

Also accepts POST (for large filter payloads).

Permission: Administrator, instructor, or group leader

URL parameters:

ParameterTypeDescription
report_idstringReport ID (e.g., quiz-results, essay-submissions)

Query parameters:

ParameterTypeDefaultDescription
course_idint0Filter by course ID
group_idint0Filter by group ID
lesson_idint0Filter by lesson ID
user_idint0Filter by user ID (auto-set for students)
statusstringallFilter by status
per_pageint-1Items per page (-1 for all)
pageint1Page 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<em>cache</em>duration()).


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

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:

ParameterTypeDefaultDescription
course_idint0Filter by course
group_idint0Filter by group
filterstringyearTime range: year, month, or week
date_fromstring""Start date in YYYY-MM-DD format
date_tostring""End date in YYYY-MM-DD format

Available Reports

Use these IDs as the report<em>id parameter in /reports/{report</em>id} requests.

Table Reports

Report IDTitleDescription
quiz-resultsQuiz ResultsQuiz attempt scores and pass/fail status per student
essay-submissionsEssay SubmissionsStudent essay submissions with grading status
assignment-statusAssignment StatusAssignment submissions with approval status
course-progressCourse ProgressStudent progress through enrolled courses
time-by-courseTime by CourseLearning time totals broken down by course
time-by-studentTime by StudentLearning time totals broken down by student
time-by-contentTime by ContentLearning time broken down by lesson or topic
instructor-performanceInstructor PerformanceCourse completion rates and student counts per instructor
group-performanceGroup PerformanceCompletion rates and activity per LearnDash group
quiz-question-analysisQuiz Question AnalysisPer-question pass/fail rates across all attempts

Chart Reports

Chart IDTitleChart TypeDescription
course-completionCourse CompletionDoughnutCompleted vs. in-progress vs. not started
top-coursesTop CoursesBarCourses ranked by enrollment or completion count
instructor-earningsInstructor EarningsBarCommission earnings per instructor over time
time-spentTime SpentBarTotal learning time with week/month/year filter
time-trendTime TrendLineLearning time trend over a selected period
enrollment-trendsEnrollment TrendsLineNew enrollments over time
revenue-per-courseRevenue per CourseBarRevenue broken down by course
course-dropoffCourse Drop-offBarWhere students stop progressing in a course

Registering Custom Reports

Use the ld<em>dashboard</em>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<em>Dashboard</em>Report<em>Base (table) or LD</em>Dashboard<em>Chart</em>Base (chart).


User Statistics Endpoint

Namespace: ldd_report/v1

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:

ParameterTypeDescription
idintWordPress user ID

Query parameters:

ParameterTypeDefaultDescription
forceboolfalseBypass 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:

FieldDescription
course_countCourses created (instructors/admins) or completed quizzes (students)
lessons_countLessons in the user’s courses
topics_countTopics in the user’s courses
quizzes_countQuizzes in the user’s courses (or completed quizzes for students)
enrolled<em>course</em>countTotal courses the user is enrolled in
active<em>course</em>countEnrolled courses with status in_progress
completed<em>course</em>countEnrolled courses with status completed
approved<em>assignment</em>countAssignments with approval_status = 1
not<em>approved</em>assignment_countAssignments pending approval
graded<em>essays</em>countEssays marked as graded
not<em>graded</em>essays_countEssays pending grading
students_countStudents enrolled in the user’s courses or groups
group_countLearnDash groups the user belongs to or manages
certificate_countCertificates earned by the user
calculated_atMySQL timestamp of when statistics were last calculated

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 StatusCodeMeaning
401rest_forbiddenNot logged in
403ld<em>dashboard</em>forbiddenInsufficient permissions
404ld<em>dashboard</em>not_foundReport or message not found
500ld<em>dashboard</em>errorServer error during data fetch
Last updated: March 4, 2026