TECHNICAL GUIDE

LearnDash Quiz Customization

Extend your LMS grading rules, attempt restrictions, profile columns, and CRM sync integrations using update-safe PHP quiz hooks.

Optimize online academy learning workflows and reporting telemetry with these update-safe LearnDash quiz hooks.

1. Dynamic Quiz Passing Thresholds

WHAT IT SOLVES:
Overrides default passing score percentage dynamically based on user metadata or attempt history.
HOOKS: learndash_quiz_passing_percentage

Overrides required quiz passing scores conditionally based on user groups or custom properties.

dynamic-quiz-passing-thresholds.php
// 1. Override Passing Threshold dynamically
add_filter('learndash_quiz_passing_percentage', 'ppunch_dynamic_quiz_passing_percentage', 10, 2);

function ppunch_dynamic_quiz_passing_percentage($passing_percentage, $quiz_post) {
    if (is_admin() || !is_user_logged_in()) {
        return $passing_percentage;
    }

    $user_id = get_current_user_id();

    // Apply a stricter passing percentage (85%) if the student is in a premium group
    if (learndash_is_user_in_group($user_id, 123)) {
        return 85;
    }

    return $passing_percentage;
}
Explanation:

Overrides simple/variable product catalog prices and adjusts cart item totals dynamically before final checkout calculations.

learndash quiz passing score dynamic passing grade custom lms grading

2. Conditional Quiz Access & Attempts Restrictor

WHAT IT SOLVES:
Disables access and hides the "Start Quiz" button dynamically if attempt thresholds are exceeded or custom prerequisites are not met.
HOOKS: learndash_show_quiz_take_button

Restricts user access to quizzes conditionally based on total attempt counts.

conditional-quiz-access--attempts-restrictor.php
// 1. Hide the Start Quiz Button based on historical attempt limits
add_filter('learndash_show_quiz_take_button', 'ppunch_restrict_quiz_access_attempts', 10, 3);

function ppunch_restrict_quiz_access_attempts($show_button, $user_id, $quiz_id) {
    if (is_admin()) {
        return $show_button;
    }

    // Fetch historical attempts from user meta
    $attempts = get_user_meta($user_id, '_sfwd-quizzes', true);
    $count = 0;

    if (is_array($attempts)) {
        foreach ($attempts as $attempt) {
            if ($attempt['quiz'] == $quiz_id) {
                $count++;
            }
        }
    }

    // Block if the user has reached 3 attempts
    if ($count >= 3) {
        return false;
    }

    return $show_button;
}
Explanation:

Blocks the quiz button from rendering if attempts logged under user meta exceed limits.

restrict learndash quiz limit quiz attempts conditional lms access

3. Non-Blocking CRM Webhook Sync

WHAT IT SOLVES:
Pushes completed quiz scores and telemetry to an external API asynchronously, guarded against duplicate triggers.
HOOKS: learndash_quiz_completed

Fires custom background webhook requests to external systems when quizzes are completed.

nonblocking-crm-webhook-sync.php
// 1. Fire Webhook Sync on Quiz Completion
add_action('learndash_quiz_completed', 'ppunch_sync_quiz_telemetry_webhook', 10, 2);

function ppunch_sync_quiz_telemetry_webhook($quiz_data, $user) {
    $user_id = $user->ID;
    $quiz_id = $quiz_data['quiz'];
    $attempt_time = $quiz_data['time'];
    
    // Guard: Prevent duplicate syncs for this specific attempt timestamp
    $sent_key = "_ppunch_webhook_sent_{$quiz_id}_{$attempt_time}";
    if (get_user_meta($user_id, $sent_key, true)) {
        return;
    }
    update_user_meta($user_id, $sent_key, 'yes');

    $webhook_url = 'https://api.yourcrm.com/v1/scores';
    $payload = [
        'user_id'    => $user_id,
        'quiz_id'    => $quiz_id,
        'percentage' => $quiz_data['percentage'],
        'score'      => $quiz_data['score'],
        'points'     => $quiz_data['points'],
    ];

    wp_remote_post($webhook_url, [
        'method'   => 'POST',
        'blocking' => false, // Non-blocking: Keeps LMS page load instant
        'body'     => json_encode($payload),
        'headers'  => [
            'Content-Type' => 'application/json',
        ],
        'timeout'  => 3,
    ]);
}
Explanation:

Sends a background, non-blocking HTTP request upon quiz completion, flagged to prevent duplicate execution.

sync learndash quiz score non blocking crm integration quiz completed webhook

4. Custom Quiz History Telemetry Columns

WHAT IT SOLVES:
Injects custom headers and populates dynamic data fields (e.g. "Time Spent") inside the user profile quiz list table.
HOOKS: learndash_profile_quiz_columns

Extends the user profile quiz history table with custom columns.

custom-quiz-history-telemetry-columns.php
// 1. Inject custom columns and data to the user profile table
add_filter('learndash_profile_quiz_columns', 'ppunch_add_profile_quiz_time_spent_column', 10, 2);

function ppunch_add_profile_quiz_time_spent_column($columns, $quiz_attempt) {
    $time = isset($quiz_attempt['time_spent']) ? intval($quiz_attempt['time_spent']) : 0;
    $time_label = 'N/A';

    if ($time > 0) {
        $minutes = floor($time / 60);
        $seconds = $time % 60;
        $time_label = "{$minutes}m {$seconds}s";
    }

    $columns['time_spent'] = [
        'id'      => 'time_spent',
        'label'   => 'Time Spent',
        'content' => $time_label,
        'class'   => 'ld-column-time-spent',
    ];

    return $columns;
}
Explanation:

Appends custom columns and inserts computed telemetry values (such as duration spent) directly into the profile quiz array.

custom learndash profile columns quiz history details lms table fields

5. Custom Post-Quiz Dynamic Routing

WHAT IT SOLVES:
Redirects students to specific URLs based on their passing or failing status.
HOOKS: learndash_quiz_redirect

Overrides post-quiz navigation redirections conditionally.

custom-postquiz-dynamic-routing.php
// 1. Override Post-Quiz Redirect Target
add_filter('learndash_quiz_redirect', 'ppunch_conditional_quiz_redirect', 10, 2);

function ppunch_conditional_quiz_redirect($redirect_url, $quiz_data) {
    if (is_admin()) {
        return $redirect_url;
    }

    $has_passed = isset($quiz_data['has_passed']) && $quiz_data['has_passed'] == 1;

    // If they failed, send them to a dedicated review page
    if (!$has_passed) {
        return site_url('/remedial-review-module/');
    }

    // If they passed, advance them directly to the next course
    return site_url('/course/advanced-curriculum/');
}
Explanation:

Inspects the passing status on submission and updates the target redirection URL.

learndash redirect after quiz conditional lms routing post quiz navigation
KNOWLEDGE BASE

Common Questions

Yes. The snippets in this guide are common examples, but we regularly build custom LearnDash logic for unique LMS requirements. Get a free quote to discuss your project with our technical lead.
// AUTHOR Muneeb K. Lead WordPress Developer

16 years building custom WordPress plugins for WooCommerce, MemberPress, and LearnDash platforms — 500+ projects delivered. Focused on performance, stability, and clean architecture.

LearnDash Quiz Solutions

Ready to extend your LearnDash quiz and scoring logic?

We build clean, update-safe quiz overrides, custom completion routing, and background CRM webhook syncs. Talk to us.

// TECH STACK

Tools We Master
Inside Out.

Starting with WordPress at the core, we use a curated stack of powerful tools to build fast, stable, and scalable systems for your business.