Email Template Customization

Get Started

Email Template Customization

LearnDash Dashboard uses a WooCommerce-style email template system introduced in version 7.5.0. Templates support theme directory overrides, merge tags, and multiple filter hooks.


How the System Works

  1. lddashboardsendemail( $templateid, $email, $args ) is called.
  2. LDDashboardEmail_Template locates the template file (theme override first).
  3. The template file is rendered with output buffering.
  4. Merge tags are replaced in the output.
  5. The content is wrapped in email-header.php and email-footer.php.
  6. Filters run on subject, content, headers, and recipient.
  7. wp_mail() sends the email.

Template Override via Theme

Copy any template from the plugin’s templates/emails/ directory to your active theme’s ld-dashboard/emails/ directory. The override takes precedence automatically.

Plugin template location:

wp-content/plugins/ld-dashboard/templates/emails/
├── email-header.php
├── email-footer.php
├── email-styles.php
├── instructor/
│   ├── application-admin-notification.php
│   ├── application-approved.php
│   └── application-rejected.php
├── student/
│   ├── course-email.php
│   ├── invitation.php
│   └── announcement-notification.php
├── withdrawal/
│   ├── request-admin-notification.php
│   ├── request-approved.php
│   └── request-rejected.php
└── messaging/
    └── new-message.php

Theme override location:

wp-content/themes/my-theme/ld-dashboard/emails/
└── instructor/
    └── application-approved.php    ← overrides plugin template

Template lookup order:

  1. Child theme: {child-theme}/ld-dashboard/emails/{template-path}
  2. Parent theme: {parent-theme}/ld-dashboard/emails/{template-path}
  3. Plugin: {plugin}/templates/emails/{template-path}

Available Merge Tags

Merge tags are replaced in both the email subject and body. They use curly brace syntax with an underscore-separated name.

Merge TagDefault ValueDescription
{site_name}WordPress site titleSite name from get_bloginfo('name')
{site_url}WordPress home URLhome_url()
{admin_email}WordPress admin emailgetoption('adminemail')
{admin_url}WordPress admin URLadmin_url()
{user_name}""Recipient display name
{user_email}""Recipient email address
{instructor_name}""Instructor display name
{student_name}""Student display name
{course_name}""Course title
{amount}""Commission/payment amount
{date}Current dateFormatted with site date format
{sender_name}""Message sender name (private messaging)
{message_url}""Direct link to the message thread
{announcement_title}""Announcement post title

Pass custom values via the $args array:

ld_dashboard_send_email(
    'instructor_approved',
    $user->user_email,
    array(
        'user_name'       => $user->display_name,
        'instructor_name' => $user->display_name,
        'site_name'       => get_bloginfo( 'name' ),
        'custom_field'    => 'My custom value', // Added via ld_dashboard_email_merge_tags
    )
);

Registered Email Types

Template IDSubjectTemplate File
instructorapplicationadminNew Instructor Registered – {user_name}instructor/application-admin-notification.php
instructor_approvedYour Instructor Application Approvedinstructor/application-approved.php
instructor_rejectedYour Instructor Application Statusinstructor/application-rejected.php
studentcourseemailMessage from {instructor_name}student/course-email.php
student_invitationYou’re Invited to {course_name}student/invitation.php
withdrawalrequestadminNew Withdrawal Request from {instructor_name}withdrawal/request-admin-notification.php
withdrawal_approvedYour Withdrawal Request Approvedwithdrawal/request-approved.php
withdrawal_rejectedYour Withdrawal Request Statuswithdrawal/request-rejected.php
newmessagenotificationNew message from {sender_name}messaging/new-message.php
announcement_notificationNew Announcement: {announcement_title}student/announcement-notification.php

Filter Hooks

Subject Filters

// Filter subject for ALL emails
add_filter( 'ld_dashboard_email_subject', function( $subject, $template_id, $args ) {
    return '[' . get_bloginfo( 'name' ) . '] ' . $subject;
}, 10, 3 );

// Filter subject for a specific template
add_filter( 'ld_dashboard_email_subject_instructor_approved', function( $subject, $template_id, $args ) {
    return $subject . ' - ' . date( 'Y' );
}, 10, 3 );

Content Filters

// Filter content for ALL emails (before header/footer wrap)
add_filter( 'ld_dashboard_email_content', function( $content, $template_id, $args ) {
    return $content . '

Powered by My Site

'; }, 10, 3 ); // Filter content for a specific template add_filter( 'ld_dashboard_email_content_student_invitation', function( $content ) { return str_replace( '{custom_promo}', 'Use code WELCOME20 for 20% off', $content ); } );

Header and Footer Filters

// Replace the email header HTML
add_filter( 'ld_dashboard_email_header', function( $header ) {
    return str_replace( 'class="ld-email-header"', 'class="ld-email-header my-custom-header"', $header );
} );

// Append to the email footer
add_filter( 'ld_dashboard_email_footer', function( $footer ) {
    $unsubscribe = '

Unsubscribe

'; return $unsubscribe . $footer; } );

Header (SMTP) Filters

// Add CC header for admin approval emails
add_filter( 'ld_dashboard_email_headers_instructor_approved', function( $headers ) {
    $headers[] = 'CC: compliance@example.com';
    return $headers;
} );

// Change From address globally
add_filter( 'ld_dashboard_email_from_address', function( $email ) {
    return 'noreply@example.com';
} );

add_filter( 'ld_dashboard_email_from_name', function( $name ) {
    return get_bloginfo( 'name' ) . ' Notifications';
} );

Recipient Filter

// Redirect all plugin emails in staging environments
add_filter( 'ld_dashboard_email_recipient', function( $to, $template_id, $args ) {
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        return 'dev@example.com';
    }
    return $to;
}, 10, 3 );

Merge Tag Filter

// Add custom merge tags
add_filter( 'ld_dashboard_email_merge_tags', function( $tags, $content, $args ) {
    $tags['company_name']   = get_option( 'my_company_name', '' );
    $tags['support_email']  = 'support@example.com';
    $tags['current_year']   = date( 'Y' );
    return $tags;
}, 10, 3 );

Add a Custom Email Type

// Register a new email type
add_filter( 'ld_dashboard_email_types', function( $types ) {
    $types['course_completed'] = array(
        'title'    => __( 'Course Completed', 'my-plugin' ),
        'template' => 'student/course-completed.php',
        'subject'  => __( 'Congratulations! You completed {course_name}', 'my-plugin' ),
    );
    return $types;
} );

Then create the template at: my-theme/ld-dashboard/emails/student/course-completed.php


Well done, !

You have successfully completed .

View Your Dashboard

Then send it:

ld_dashboard_send_email(
    'course_completed',
    $student->user_email,
    array(
        'user_name'   => $student->display_name,
        'course_name' => $course->post_title,
    )
);

Email Queue System

The plugin uses a database-backed email queue for bulk sends (instructor email broadcasts). The queue is stored in {prefix}lddashboardemail_queue.

Queue table columns:

ColumnDescription
idAuto-increment primary key
user_idSender user ID
recipient_emailRecipient email address
recipient_nameRecipient display name
email_subjectEmail subject
email_messageFull HTML email body
headersSerialized headers array
template_idTemplate ID used to generate this email
statuspending, sent, or failed
attemptsNumber of send attempts
max_attemptsMaximum retries (default: 3)
error_messageLast error message if failed
scheduled_atWhen the email is scheduled to send
sent_atTimestamp of successful send
created_atQueue insertion timestamp

Processing:

A WordPress cron job processes the queue. Emails are processed in batches. Failed sends are retried up to max_attempts times with exponential backoff. The plugin also runs a daily cleanup cron to remove old sent and failed records.


Header and Footer Customization

To customize the shared header and footer that wrap all emails, copy the files to your theme:

my-theme/ld-dashboard/emails/
├── email-header.php    ← wrap start, logo, background
└── email-footer.php    ← copyright, social links, wrap end

The header template receives $email_subject as a local variable (used for the HTML </code> tag).</p> <p class="wp-block-paragraph">Both header and footer templates are also filterable via <code>ld<em>dashboard</em>email<em>header</code> and <code>ld</em>dashboard<em>email</em>footer</code> filters (see above).</p> <hr class="wp-block-separator has-alpha-channel-opacity"/> <h2 class="wp-block-heading">Enrollment Notification Example</h2> <p class="wp-block-paragraph">Customize the instructor-approved email to include a direct dashboard link:</p> <ol class="wp-block-list"> <li>Copy <code>templates/emails/instructor/application-approved.php</code> to: <code>my-theme/ld-dashboard/emails/instructor/application-approved.php</code></li> <li>Edit the template:</li> </ol> <pre class="wp-block-code"><code><?php $user_name = isset( $template_args['user_name'] ) ? $template_args['user_name'] : ''; $dashboard_url = Ld_Dashboard_Functions::instance()->ld_dashboard_get_url( 'dashboard' ); ?> <h2><?php printf( esc_html__( 'Welcome, %s!', 'ld-dashboard' ), esc_html( $user_name ) ); ?></h2> <p><?php esc_html_e( 'Your instructor application has been approved.', 'ld-dashboard' ); ?></p> <p><?php esc_html_e( 'You can now create courses and manage students from your dashboard.', 'ld-dashboard' ); ?></p> <p style="text-align:center;margin-top:24px;"> <a href="<?php echo esc_url( $dashboard_url ); ?>" style="background:#2067fa;color:#fff;padding:12px 28px;text-decoration:none;border-radius:4px;display:inline-block;"> <?php esc_html_e( 'Go to Your Dashboard', 'ld-dashboard' ); ?> </a> </p></code></pre> <p class="wp-block-paragraph">The override is detected automatically — no code changes required.</p> </div> </article> <!-- Last Updated --> <div class="docs-meta"> <span class="docs-last-updated"> Last updated: March 4, 2026 </span> </div> <!-- Post Navigation --> <nav class="docs-post-navigation"> <div class="docs-nav-prev"> <a href="https://docs.wbcomdesigns.com/docs/learndash-dashboard/developer-guide-ldd/custom-tabs-ldd/" class="docs-nav-link-prev"> <span class="docs-nav-label">Previous</span> <span class="docs-nav-title">Creating Custom Tabs</span> </a> </div> <div class="docs-nav-next"> </div> </nav> </div> </main> </div> </div> </div><!-- .container --> <footer id="colophon" class="site-footer"> <div class="site-footer-wrapper"> <div class="container"> </div><!-- .container --> </div><!-- .site-footer-wrapper --> <div class="site-info"> <div class="container"> Copyright © 2026 Wbcom Designs </div> <a class="privacy-policy-link" href="https://docs.wbcomdesigns.com/privacy-policy/" rel="privacy-policy">Privacy Policy</a></div><!-- .site-info --> </footer><!-- #colophon --> </div><!-- #page --> <div class="mobile-menu-close"></div> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/knowx-child/*","/wp-content/themes/knowx/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script> <script id='kirki-viewport-lists'>var kirkiViewports = {"md":{"value":1200,"scale":1,"minWidth":1200,"maxWidth":1200,"title":"Desktop","icon":"desktop","activeIcon":"desktop-hover","id":"md","type":"max"},"tablet":{"value":991,"scale":1,"minWidth":991,"maxWidth":991,"title":"Tablet","icon":"tablet-default","activeIcon":"tablet-hover","type":"max","id":"tablet"},"mobileLandscape":{"value":767,"scale":1,"minWidth":767,"maxWidth":767,"title":"Landscape","icon":"phone-hr-default","activeIcon":"phone-hr-hover","type":"max","id":"mobileLandscape"},"mobile":{"value":575,"scale":1,"minWidth":575,"maxWidth":575,"title":"Mobile","icon":"phone-vr-default","activeIcon":"phone-vr-hover","type":"max","id":"mobile"}};</script><script id='kirki-variable-lists'>var kirkiCSSVariable = {"data":[{"title":"Colors","key":"color","modes":[{"title":"Default","key":"default"}],"variables":[]},{"title":"Numbers","key":"size","modes":[{"title":"Default","key":"default"}],"variables":[]},{"title":"Text Styles","key":"text-style","modes":[{"title":"Default","key":"default"}],"variables":[]},{"title":"Font Family","key":"font-family","modes":[{"title":"Default","key":"default"}],"variables":[]}]};</script><script id="kirki-api-and-nonce"> window.wp_kirki = { ajaxUrl: "https://docs.wbcomdesigns.com/wp-admin/admin-ajax.php", restUrl: "https://docs.wbcomdesigns.com/wp-json/", siteUrl: "https://docs.wbcomdesigns.com", apiVersion: "v1", postId: "1495635", nonce: "3e0355f3e4", call_from: "", templateId: "", context: {"id":1495635,"type":"post"} }; </script> <!-- Google Tag Manager (noscript) snippet added by Site Kit --> <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-NBL3GF8" height="0" width="0" style="display:none;visibility:hidden"></iframe> </noscript> <!-- End Google Tag Manager (noscript) snippet added by Site Kit --> <!-- Sign in with Google button added by Site Kit --> <style> .googlesitekit-sign-in-with-google__frontend-output-button{max-width:320px} .interim-login #login>.googlesitekit-sign-in-with-google__frontend-output-button{margin-bottom:16px} </style> <script src="https://accounts.google.com/gsi/client"></script> <script data-siwg-config="{"clientID":"469862705962-p5obbliq4hjtdumm558v74clncdnpimf.apps.googleusercontent.com","defaultButtonOptions":{"theme":"outline","text":"signin_with","shape":"rectangular"},"loginURI":"https:\/\/docs.wbcomdesigns.com\/wp-login.php?action=googlesitekit_auth","isUserLoggedIn":false,"isWPLogin":false,"isPreview":false,"isWooCommerce":false,"isExistingUserFlow":false,"connectNonce":"","followsPostRedirect":false,"redirectTo":"","redirectCookieName":"googlesitekit_auth_redirect_to","redirectCookiePath":"\/","redirectCookieTTL":0,"shouldShowOneTapPrompt":false}" src="https://docs.wbcomdesigns.com/wp-content/plugins/google-site-kit/dist/assets/js/sign-in-with-google-15a07dd57b5f79886b45.js"></script> <!-- End Sign in with Google button added by Site Kit --> <script> /(trident|msie)/i.test(navigator.userAgent)&&document.getElementById&&window.addEventListener&&window.addEventListener("hashchange",function(){var t,e=location.hash.substring(1);/^[A-z0-9_-]+$/.test(e)&&(t=document.getElementById(e))&&(/^(?:a|select|input|button|textarea)$/i.test(t.tagName)||(t.tabIndex=-1),t.focus())},!1); </script> <script id="crisp-js-before"> window.$crisp=[]; if (!window.CRISP_RUNTIME_CONFIG) { window.CRISP_RUNTIME_CONFIG = {} } if (!window.CRISP_RUNTIME_CONFIG.locale) { window.CRISP_RUNTIME_CONFIG.locale = 'en-us' } CRISP_WEBSITE_ID = '08d857de-573a-4b16-a743-46cc8d9f046d'; //# sourceURL=crisp-js-before </script> <script id="crisp-js" async src="https://client.crisp.chat/l.js"></script> <script id="custom-script-js-extra"> var knowxVars = {"ajaxUrl":"https://docs.wbcomdesigns.com/wp-admin/admin-ajax.php","nonce":"ef6a4b9ce5"}; //# sourceURL=custom-script-js-extra </script> <script id="custom-script-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx-child/js/custom.js"></script> <script id="knowx-navigation-js-extra"> var knowxScreenReaderText = {"expand":"Expand child menu","collapse":"Collapse child menu"}; //# sourceURL=knowx-navigation-js-extra </script> <script id="knowx-navigation-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/navigation.min.js" async></script> <script id="knowx-superfish-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/superfish.min.js" async></script> <script id="knowx-isotope-pkgd-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/isotope.pkgd.min.js" async></script> <script id="knowx-fitvids-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/fitvids.min.js" async></script> <script id="knowx-sticky-kit-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/sticky-kit.min.js" async></script> <script id="knowx-custom-js-extra"> var knowx_data = {"enable_scrollup":""}; //# sourceURL=knowx-custom-js-extra </script> <script id="knowx-custom-js" src="https://docs.wbcomdesigns.com/wp-content/themes/knowx/assets/js/custom.min.js" async></script> <script id="googlesitekit-consent-mode-js" src="https://docs.wbcomdesigns.com/wp-content/plugins/google-site-kit/dist/assets/js/googlesitekit-consent-mode-86cb52dcb9f2b27ed244.js"></script> <script id="wp-emoji-settings" type="application/json"> {"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://docs.wbcomdesigns.com/wp-includes/js/wp-emoji-release.min.js"}} </script> <script type="module"> /*! This file is auto-generated */ var e="script#wp-emoji-settings",t=document.querySelector(e);if(!(t instanceof HTMLScriptElement))throw new Error("Element missing: "+e);const r=JSON.parse(t.text),s=(window._wpemojiSettings=r,"wpEmojiSettingsSupports"),o=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(s,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const r=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===r[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,r){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&&!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!r(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,r){let a;const s=(a="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),o=(s.textBaseline="top",s.font="600 32px Arial",{});return e.forEach(e=>{o[e]=t(s,e,n,r)}),o}function a(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}r.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(s));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&&"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof URL&&URL.createObjectURL&&"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(o),u.toString(),c.toString(),p.toString()].join(",")+"));",r=new Blob([e],{type:"text/javascript"});const a=new Worker(URL.createObjectURL(r),{name:"wpTestEmojiSupports"});return void(a.onmessage=e=>{i(n=e.data),a.terminate(),t(n)})}catch(e){}i(n=f(o,u,c,p))}t(n)}).then(e=>{for(const n in e)r.supports[n]=e[n],r.supports.everything=r.supports.everything&&r.supports[n],"flag"!==n&&(r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&r.supports[n]);var t;r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&!r.supports.flag,r.supports.everything||((t=r.source||{}).concatemoji?a(t.concatemoji):t.wpemoji&&t.twemoji&&(a(t.twemoji),a(t.wpemoji)))}); //# sourceURL=https://docs.wbcomdesigns.com/wp-includes/js/wp-emoji-loader.min.js </script> <div hidden data-wpu-integrity="84af466d45ebb6761bc50bcd5ed59669"></div> </body> </html> <!-- plugin=object-cache-pro client=phpredis metric#hits=4954 metric#misses=23 metric#hit-ratio=99.5 metric#bytes=3184457 metric#prefetches=0 metric#store-reads=234 metric#store-writes=10 metric#store-hits=491 metric#store-misses=10 metric#sql-queries=13 metric#ms-total=386.18 metric#ms-cache=59.99 metric#ms-cache-avg=0.2469 metric#ms-cache-ratio=15.5 -->