Skip to content

Hooks & filters ​

This page lists every public action and filter in Lumtera 1.3.0 and Lumtera Pro 1.2.0, grouped by area. Parameters are listed in the order the hook passes them. When you use more than one, pass the count to add_filter() or add_action().

Checks and scanning ​

HookTypeParametersPurpose
lumtera_rule_classesfilterstring[] $classesFully qualified class names of the checks to load. Add or remove checks. See Custom checks.
lumtera_register_rulesactionRuleRegistry $rulesFires after the built-in checks are registered. Register check instances with $rules->register(). The same ID replaces a check.
lumtera_measured_rule_classesfilterstring[] $classesMeasuredRule classes: checks whose findings come from the browser engine in review mode and Pro page checks. Add-ons with their own engine measures add their classes here. Lumtera Pro's page measures and form tests use it.
lumtera_post_scannedactionint $post_id, Report $report, array|null $previousAfter a post is checked and its results stored: on save, after an Elementor save, after a dismissal, in Check all content, the REST scan route, wp lumtera scan, the check-post ability, after a fix is applied, and Pro's re-checks after an ignore rule changes. $previous is the previous summary, or null on the first check.
lumtera_bulk_batch_doneactionbool $doneAfter each batch of Check all content, and once at the end of wp lumtera scan. $done is true when the whole run has finished.
lumtera_bulk_scan_doneactionarray $totalsWhen a Check all content run has finished: the last batch of the Overview's bulk check, or wp lumtera scan --all. $totals are the site-wide totals after the run.
lumtera_pre_render_contentfilterstring|null $html, string $content, int $post_idReturn HTML to check instead of rendering the post content. Only runs for saved posts. Lumtera's own page builder integrations (Elementor, Divi, Beaver Builder, Bricks, Oxygen, WPBakery) use it. The HTML you return is used as it is.
lumtera_rendered_contentfilterstring $html, int $post_idChange the rendered HTML before it's checked. $post_id is 0 for unsaved content. Lumtera's Advanced Custom Fields and WooCommerce integrations use it to add field values and the product short description.
lumtera_the_content_meta_keysfilterstring[] $keys, int $post_idPost meta keys that mark a post as built by a page builder that adds its layout through the_content
lumtera_render_with_the_contentfilterbool $built, int $post_idWhether Lumtera renders a post through the_content, for builders without their own integration. Default: true when one of the meta keys above is set. Posts with empty or placeholder content are rendered this way anyway.
lumtera_managed_by_builderfilterstring|null $name, int $post_idThe page builder that manages a post, for builders Lumtera doesn't know. Return its name, and Lumtera won't change the post's content directly. Default null.
lumtera_review_htmlfilterstring $content, int $post_idThe content HTML as it reaches the browser, used to map issues to elements in review mode. For plugins that wrap the content in markers they remove later (TranslatePress, for example).
lumtera_max_check_bytesfilterint $bytes (default 524288)The largest content accepted for a live check. At least 1024.
lumtera_check_rate_limitfilterint $limit (default 120)Live checks per user per minute. 0 turns the limit off.
lumtera_list_limitfilterint $limit (default 50)How many findings of each check the editor sidebar, review mode and the Content report list before Show all. At least 1. Counts and scores always cover every finding.
lumtera_readability_languagefilterstring $lang, int $post_idTwo-letter language code used to pick the reading-level formula. Default: the post's language in Polylang or WPML, otherwise the site language. $post_id is 0 when unknown.
lumtera_readability_supportedfilterbool $supported, string $langWhether to measure a reading level. Default: true for en, es, fr, de, it and nl. Forcing it on for another language uses the English formula.
lumtera_link_to_document_messagefilterstring $message, string $href, string $extMessage for the link-to-document check
lumtera_new_window_phrasesfilterstring[] $phrasesLowercase phrases that count as warning that a link opens a new tab, for the link-new-window check
lumtera_vague_link_phrasesfilterstring[] $phrases, string $langLink texts that say nothing on their own ("click here"), per two-letter language code, for the link-ambiguous-text check. Adding texts for a language without a built-in list makes its matches "Needs review" instead of tips.
lumtera_sensory_phrasesfilterstring[] $phrases, string $langComplete PCRE patterns (with delimiters and flags) the sensory-language check looks for, per language prefix. Empty for every language except English by default.

Example: check a custom field ​

Add a custom field's HTML to what Lumtera checks:

php
add_filter( 'lumtera_rendered_content', function ( string $html, int $post_id ): string {
	if ( $post_id && 'event' === get_post_type( $post_id ) ) {
		$html .= wp_kses_post( get_post_meta( $post_id, 'event_details', true ) );
	}
	return $html;
}, 10, 2 );

Advanced Custom Fields values are added already. You only need this for other fields.

Example: accept phrases in another language ​

php
add_filter( 'lumtera_new_window_phrases', function ( array $phrases ): array {
	$phrases[] = 'nueva pestaña';
	return $phrases;
} );

add_filter( 'lumtera_vague_link_phrases', function ( array $phrases, string $lang ): array {
	if ( 'de' === $lang ) {
		$phrases[] = 'hier klicken';
	}
	return $phrases;
}, 10, 2 );

Example: react to new errors ​

php
add_action( 'lumtera_post_scanned', function ( int $post_id, $report, ?array $previous ) {
	$errors = $report->to_array()['counts']['error'];
	if ( $errors > ( $previous['errors'] ?? 0 ) ) {
		// Errors went up on this save.
	}
}, 10, 3 );

Site parts ​

Site parts are the template parts, synced patterns, navigation menus and widget areas every page shares. Lumtera checks them on their own, so an issue in the header is reported once.

HookTypeParametersPurpose
lumtera_scan_site_partsfilterarray $partsThe site parts Lumtera checks. Add a part a plugin prints, or leave one out. Each part is { type, key, label, blocks } plus either markup (block markup when blocks is true, otherwise HTML) or render (a callable that returns HTML). type must be one of Lumtera's source types, such as template_part, menu or widget. Up to 200 parts are checked.
lumtera_parts_scan_doneactionarray $resultAfter every site part was checked and the findings stored, from the Overview or wp lumtera scan --parts. $result is { checked, issues, failed, skipped }. failed maps each part to the reason it couldn't be checked.

Whole-page results and audit mode ​

Review mode's whole-page checks and Lumtera Pro's page checks run in a browser, on a page loaded in audit mode (?lumtera-audit=<nonce>). See Accessibility checks in CI: Audit mode.

HookTypeParametersPurpose
lumtera_audit_scriptsactionstring $coreWhen audit mode adds the engine to a page. Enqueue your own measures here: scripts that depend on $core (lumtera-audit-core) and call lumteraAudit.register(). Lumtera Pro's page measures and form tests use it.
lumtera_signed_check_requestfilterbool $signed (default false), string $contextWhether this request is a check an add-on verified itself (with a signed, one-time pass), so it gets audit mode ($context audit) or source markers (markers) without a signed-in editor. Return true only for a request you verified. Lumtera Pro's signed-in checks use it.
lumtera_max_page_bytesfilterint $bytes (default 3 MB)The largest page snapshot the whole-page checks may send to the server
lumtera_page_results_max_rowsfilterint $rows (default 500)Most whole-page results kept: one per address, screen width and role. Older rows are removed first.
lumtera_page_result_summariesfilterarray $rows (default empty), array $filtersSince 1.2.0. Whole-page results from other sources, without their findings, so site-wide numbers stay light. Each row: url, source, origin, viewport, width, role, post_id, engine, checked_at (Unix time), checked_by, and either ref (any scalar your lumtera_page_result_load filter reads the row by) or summary (from PageResults::summarise()). $filters holds what was asked for (url, post_id, limit). Lumtera Pro's page checks use it. See Whole-page results for add-ons.
lumtera_page_result_loadfilterarray $found (default empty), string $origin, array $refs, string $whatSince 1.2.0. Return the summaries or findings of your rows by the ref your lumtera_page_result_summaries rows carry, as ref => value. $what is summary (return the PageResults::summarise() shape) or issues (return the findings array). $origin is your rows' origin, for example pro. Lumtera asks for a few rows at a time. Lumtera Pro's page checks use it.
lumtera_page_summary_backfilledactionstring $table, int $idSince 1.2.0. Fires once for each saved whole-page result that gets its summary in the background (results saved before 1.2.0). $table is the prefixed table name, Lumtera's or an add-on's of the same shape.
lumtera_page_result_sourcesfilterarray $rows (default empty), array $filtersOlder way to add whole-page results, with all their findings. Each row: url, issues, source, origin, viewport, role, post_id, checked_at. $filters holds what was asked for (url, post_id, limit) and, since 1.2.0, summaries => true when Lumtera has already read your rows through lumtera_page_result_summaries. In that case return nothing more. It still works, but loads every finding into memory. New add-ons should use lumtera_page_result_summaries and lumtera_page_result_load.
lumtera_review_listing_pagefilterint $post_id (0 for none)The page that stands for a listing in review mode (its edit link and guided checklist): by default the posts page on the blog home. Lumtera's WooCommerce integration returns the shop page for product archives. Return a page ID for your own listings.
lumtera_page_featuresfilterarray|null $found, int $post_idWhat a post's page contains (forms, video, search…), used to suggest checklist items that don't apply. Return [ 'features' => array<string, bool>, 'time' => int ] to replace an older record. Lumtera Pro's page checks use it.
lumtera_help_lexiconfilterarray $lexiconWords that mark a link as a way to get help (WCAG 3.2.6), by kind: contact, help, support and faq. Lowercase, matched as whole words in the link's name or as a segment of its path.

Whole-page results for add-ons ​

Since Lumtera 1.2.0, whole-page results are read through \Lumtera\PageResults. It merges review mode's saved results with results from add-ons (Lumtera Pro's page checks are one) and keeps memory flat however many pages a site has checked: site-wide readers get a compact summary of each page, and the findings themselves are read only for the few pages shown in full.

Reading results ​

MethodReturns
PageResults::index( array $filters = [] )The results that stand, newest first, without their findings: one row per page (address and role), or per page and viewport with per_viewport. latest(), summaries() and walk() all start here, so they agree on which rows stand.
PageResults::summaries( array $filters = [] )The rows of index(), each with counts ({ error, warning, notice } of its open findings) and dismissed (how many findings a dismissal hides). No findings are kept in memory.
PageResults::walk( array $rows, callable $callback )Goes through rows of index() 50 at a time and calls $callback( array $row, array $open, int $dismissed ) for each. $open lists the row's open findings (dismissals already applied), each as { fingerprint, severity, rule, criteria, source }, where source is the finding's source record or null. Use it for your own site-wide numbers.
PageResults::with_issues( array $rows )The rows you pass (from index() or summaries()) with their findings, keys kept: issues (the open findings), dismissed (the findings a dismissal hides) and counts. For the rows you show in full, such as one screenful of a list.
PageResults::latest( array $filters = [] )with_issues( index( $filters ) ): every row with all its findings. Use it for one page or a few, not site-wide.

$filters can hold:

KeyMeaning
urlOnly this address
post_idOnly this post's page
limitMost rows returned. Default and maximum 1000.
per_viewporttrue keeps one row per page and viewport, instead of one per page
originfree or pro: only rows from that origin

Each row of index() has:

KeyMeaning
url, url_hashThe page's normalized address and its MD5
post_idThe post the page shows, or 0
viewport, widthdesktop, tablet or phone, and the width it was checked at (0 when not known)
roleThe role a signed-in check used, empty for a logged-out page
sourceHow it was checked: review (review mode), scheduled or browser (Lumtera Pro), or an add-on's own value
originWho stored it: free, pro or an add-on's origin
engineThe engine that produced it
issuesnull, or the findings of a row added through the older lumtera_page_result_sources filter
checked_at, checked_byUnix time, and the user ID (0 for none)
refHow Lumtera reads its summary or findings later. Treat it as opaque.

When a page has several rows, one stands: Lumtera Pro's scheduled (logged-out) check first, then its browser check at desktop width, then review mode's desktop result, then any other row. Among rows of the same rank, the newest wins.

php
// Pages with open errors, from every source, without loading their findings.
$pages = array_filter(
	\Lumtera\PageResults::summaries(),
	fn( array $row ) => $row['counts']['error'] > 0
);

Adding your own results ​

An add-on that stores its own whole-page results joins them through two filters, so they show in the Overview, the weekly email, coverage, the abilities and Lumtera Pro's conformance report:

  1. lumtera_page_result_summaries: return your rows without their findings, in the row shape above. Give each a ref (any scalar, such as your row ID) or a ready summary. Lumtera leaves out rows that don't match the url or post_id asked for, but filtering in your own query and keeping to limit keeps it light.
  2. lumtera_page_result_load: when Lumtera needs some of your rows' summaries or findings, it passes your origin, the refs and $what (summary or issues). Return ref => value. It asks for a few rows at a time.

A summary is the shape PageResults::summarise( array $items, array $counts ) returns: $items are the stored findings (each with at least rule, severity and fingerprint) and $counts the row's error, warning and notice counts. Build it with that method rather than by hand.

If your table has the same columns as Lumtera's (id, issues, errors, warnings, notices and summary), two more public methods do the work for you:

  • PageResults::load_rows( string $table, array $ids, string $what, bool $summary_column = true ) reads rows' summaries or findings by ID. A summary that's missing or out of date is made from the findings as it's read. Pass false for $summary_column while your table has no summary column yet.
  • PageResults::backfill( string $table, float $seconds = 20.0 ) writes the missing summaries of older rows, oldest first, within the time budget. It returns true when none is left, false when the time ran out, and null when another run holds the lock. Run it from your own scheduled job until it returns true. lumtera_page_summary_backfilled fires for each summary it writes.
php
// An add-on that keeps results in its own table of the same shape.
add_filter( 'lumtera_page_result_summaries', function ( array $rows, array $filters ) {
	global $wpdb;
	$limit = min( 1000, (int) ( $filters['limit'] ?? 1000 ) );
	$found = $wpdb->get_results( $wpdb->prepare(
		'SELECT id, url, checked_at FROM %i ORDER BY checked_at DESC LIMIT %d',
		$wpdb->prefix . 'acme_pages',
		$limit
	), ARRAY_A );

	foreach ( $found as $row ) {
		$rows[] = [
			'url'        => $row['url'],
			'source'     => 'acme',
			'origin'     => 'acme',
			'viewport'   => 'desktop',
			'engine'     => 'acme-scanner',
			'checked_at' => strtotime( $row['checked_at'] . ' UTC' ),
			'ref'        => (int) $row['id'],
		];
	}
	return $rows;
}, 10, 2 );

add_filter( 'lumtera_page_result_load', function ( array $found, string $origin, array $refs, string $what ) {
	if ( 'acme' !== $origin ) {
		return $found;
	}
	global $wpdb;
	return \Lumtera\PageResults::load_rows( $wpdb->prefix . 'acme_pages', $refs, $what ) + $found;
}, 10, 4 );

The older lumtera_page_result_sources filter still works: rows added there carry all their findings, at the old memory cost. Lumtera passes summaries => true in its $filters when it has already read rows through lumtera_page_result_summaries, so an add-on that answers both returns nothing more there.

WP-CLI ​

HookTypeParametersPurpose
lumtera_cli_stored_page_resultsfilterarray $results (default empty), string $urlStored browser results for a page, added by wp lumtera check --page --stored. Each item: rule (check ID), severity, message, context (markup) and optionally fingerprint. Items without a known rule ID or severity are left out. Lumtera adds the results saved from review mode here.

wp lumtera scan also fires lumtera_bulk_batch_done, lumtera_bulk_scan_done and, with --parts, lumtera_parts_scan_done.

Dismissing and ignoring ​

HookTypeParametersPurpose
lumtera_issue_dismissedactionint $post_id, string $fingerprint, string $rule_id, string $noteAfter an issue is dismissed on a post
lumtera_issue_restoredactionint $post_id, string $fingerprint, string $rule_idAfter a dismissed issue is restored
lumtera_ignored_issuefilterarray|null $record, Issue $issue, int $post_idHide a finding wherever it appears, not just on one post. Return null to leave it open, or a record to report it as dismissed. $post_id is 0 for a whole page.
lumtera_false_positive_urlfilterstring $urlWhere Report a false positive links go. Default: Lumtera's support forum on WordPress.org. A GitHub new-issue URL (…/issues/new) is prefilled with a title and the text. Any other URL opens as it is, with the text shown to copy.

A record returned from lumtera_ignored_issue can have these keys:

KeyTypeMeaning
reasonstringWhy it's ignored. Shown as the dismissal note.
userintWho ignored it
timeintWhen, as a Unix time
sourcestringWhere the record comes from. Default global.
severitystringOptional. The most severe finding it may hide: a record with warning never hides an error.
can_restoreboolOptional. Whether the current user may undo it.
restore_pathstringOptional. A REST path that undoes it with DELETE.
expiresintOptional. When it stops applying, 0 for never.

Ignored findings aren't stored as open issues, so they don't count in scores or reports. Lumtera Pro's ignore rules use this filter, and leave a record another plugin already returned unchanged.

php
// Hide the vague "Read more" links a theme prints on every archive card.
add_filter( 'lumtera_ignored_issue', function ( $record, \Lumtera\Issue $issue ) {
	if ( null === $record && 'link-ambiguous-text' === $issue->rule_id
		&& str_contains( $issue->context, 'class="card__more"' ) ) {
		return [ 'reason' => 'The card heading gives the link context.', 'severity' => 'warning' ];
	}
	return $record;
}, 10, 2 );

Editor's rendered page check ​

HookTypeParametersPurpose
lumtera_editor_page_checkfilterbool $enabled, int $post_idWhether editors can check the rendered page from the editor. false removes the button and the check after each save, and no page is loaded or sent.
lumtera_editor_page_check_after_savefilterbool $after_saveWhether the rendered page is checked after each save until an editor changes their own choice. Default true.
lumtera_contrast_assume_themefilterbool $assumeWhether the editor's contrast check falls back to the theme's global styles when a post leaves a color unset. false checks only pairs the content sets itself.
php
// Off for the whole site, until someone turns it on for themselves.
add_filter( 'lumtera_editor_page_check_after_save', '__return_false' );

Visitor pages and settings files ​

HookTypeParametersPurpose
lumtera_visitor_requestfilterbool $visitorWhether this request gets only Lumtera's visitor-page code. Return false if your plugin calls Lumtera on logged-out front-end pages in a way that doesn't go through its hooks. Runs on plugins_loaded, so add it when your plugin loads.
lumtera_rule_classesfilterstring[] $classesRule classes to register. Runs when the checks are first needed: add it when your plugin loads, or on init at the latest.
lumtera_settings_transfer_itemsfilterarray $itemsSettings that travel in the export file, as option name => {label, sanitize, drop}. List every key that holds an address, a key or a token in drop: the file may be shared.

Fixes ​

Fixes are proposed, reviewed and applied as changes. See Fixing issues.

HookTypeParametersPurpose
lumtera_change_allowedfilterbool|WP_Error $allowed (default true), string $action, array $change, int $user_idWhether a person may approve, reject, apply or revert a change. Return a WP_Error to refuse with your own message. Lumtera Pro's fix approvals use it.
lumtera_change_appliedactionarray $changeAfter a change is written to a post and the post checked again. $change['status'] is applied.
lumtera_change_revertedactionarray $changeAfter an applied change is undone. $change['status'] is reverted.
php
// Only editors may apply fixes to pages.
add_filter( 'lumtera_change_allowed', function ( $allowed, string $action, array $change, int $user_id ) {
	if ( true === $allowed && 'apply' === $action && 'page' === get_post_type( (int) $change['post_id'] )
		&& ! user_can( $user_id, 'edit_others_pages' ) ) {
		return new WP_Error( 'acme_pages', 'Ask an editor to apply fixes to pages.' );
	}
	return $allowed;
}, 10, 4 );

Guided checklists ​

HookTypeParametersPurpose
lumtera_manual_test_savedactionint $post_id, string $test, string $result, array|null $recordAfter a checklist result is recorded or cleared on a post, once it's stored. One call per checklist item. $test is a WCAG criterion number, such as 2.1.4. $result is pass, fail or na, and empty when cleared. $record is { result, note, failed, user, time }, or null when cleared.

Statement and exceptions ​

See Accessibility statement.

HookTypeParametersPurpose
lumtera_statement_savedactionint $page_id, array $dataAfter the statement is written to a page, once the page and the answers are saved. $data holds the sanitized answers.
lumtera_statement_asideactionarray $answersIn the statement screen's side column. Lumtera Pro's burden records use it.
lumtera_statement_burden_itemsfilterstring[] $items (default empty), array $answersPublic summaries of disproportionate burden assessments, one plain-text line each, for the statement's burden section. Lumtera Pro's burden records use it.
lumtera_statement_method_itemsfilterstring[] $items (default empty), array $answersMore ways the site was evaluated, one plain-text line each, for the statement's "how we evaluated" list
lumtera_statement_has_feedback_linkfilterbool $linked, array $savedWhether the statement links to a way to give feedback. Default: whether a feedback form link is set.
lumtera_exception_taggedactionint $post_id, array|null $tag, string $previousAfter an exception tag is set, changed or removed on a post or attachment. $tag is { type, note, tagged_by, date }, or null when removed. $previous is the type it had before, empty when it had none.

Feedback ​

See Accessibility feedback.

HookTypeParametersPurpose
lumtera_feedback_receivedactionint $id, array $dataAfter a visitor's feedback is stored. $data holds reference, type, page_url, post_id and reply_format, never the name, email address or message.
lumtera_feedback_updatedactionint $id, string $status, string $oldAfter a feedback record changes status: new, in_progress, answered or closed
lumtera_feedback_is_spamfilterbool $spam, array $dataWhether a message is spam. Spam isn't stored, and the sender sees the usual thank-you. $data holds the cleaned fields.
lumtera_feedback_rate_limitfilterint $limit (default 5)Messages one IP address may send per hour. 0 turns the limit off.
lumtera_feedback_reply_tofilterstring $addressThe Reply-To address of feedback replies sent from the inbox. Default: the contact email in your accessibility statement, otherwise the replying user's email. An invalid address is dropped.
lumtera_feedback_allowed_hostsfilterstring[] $hosts (default empty)Extra host names, such as example.com, that the feedback form may be sent from. The form refuses a message whose browser Origin header names another site; the site's home and site address hosts, and the host the request came in on, are always allowed, with or without www.. Add a host here when the form is embedded on another domain you own.
lumtera_feedback_client_ipfilterstring $ipThe visitor IP address the limit counts. Default: REMOTE_ADDR. Behind a proxy that sets a trusted header, return that address.
lumtera_feedback_capabilityfilterstring $capabilityCapability needed to read and answer feedback. Default lumtera_manage_feedback (editors and administrators).
lumtera_feedback_query_argsfilterarray $args, array $filtersThe inbox's WP_Query arguments, for example to show only overdue items
lumtera_feedback_viewsfilterarray $views (default empty)Extra inbox views, as slug => label. Lumtera Pro adds Overdue.
lumtera_feedback_badgesfilterarray $badges (default empty), array $itemTags shown next to a record's status. Each is { text, tone }, where tone is crit, warn, good or empty.
lumtera_feedback_noticesfilterarray $okConfirmations the inbox can show after an action, as code => text. Add yours when you add an action.
lumtera_feedback_errorsfilterarray $errorsError messages the inbox can show after an action, as code => text
lumtera_feedback_item_detailsactionarray $itemUnder a feedback record's details, for example for an assignee
lumtera_feedback_item_actionsactionarray $itemIn a feedback record's status card, for more actions
php
// Send new feedback to a help desk. The message itself stays in WordPress.
add_action( 'lumtera_feedback_received', function ( int $id, array $data ) {
	wp_remote_post( 'https://helpdesk.example.com/hooks/a11y', [
		'body' => [ 'reference' => $data['reference'], 'page' => $data['page_url'] ],
	] );
}, 10, 2 );

Permissions ​

HookTypeDefaultControls
lumtera_capabilityfilterlumtera_view_reportsThe Overview, Content report, the Upgrade to Pro screen, the Dashboard widget, /site-summary, the site-summary ability, and Pro's report screens and routes (page checks, documents, reports, the conformance report, creating tracker issues)
lumtera_can_check_allfiltertrueWhether the current user may run Check all content (the Overview's buttons and the /lumtera/v1/bulk route). Only asked for people who pass lumtera_capability. Lumtera Pro's read-only client role turns it off.
lumtera_dismiss_errors_capabilityfilterlumtera_dismiss_errorsDismissing and restoring errors, and (Pro) closing an error task as Won't fix
lumtera_review_capabilityfilterlumtera_review_modeReview mode on the front end, on top of edit_post for the post, and audit mode
lumtera_images_capabilityfilterupload_filesOpening the alt text screen. Editing an image still needs edit_post on it.
lumtera_feedback_capabilityfilterlumtera_manage_feedbackReading and answering feedback
lumtera_permission_defaultsfiltercapability => role namesThe default grants: the roles each capability goes to before anyone changes the setting. An add-on that makes its own role adds it here. Lumtera Pro adds its "Accessibility client" role.

The lumtera_* capabilities are given to roles under Lumtera → Settings → Permissions. By default, roles that can edit others' posts get lumtera_view_reports, lumtera_dismiss_errors and lumtera_manage_feedback, and roles that can edit posts get lumtera_review_mode. Administrators always have all of them. A filter wins over the settings. See Roles & permissions.

Pro Lumtera Pro 1.1.0 adds lumtera_manage_reports (Manage client reports). It's needed to create and delete client reports, create and revoke share links and save the conformance report. Opening and downloading reports needs only lumtera_view_reports. Every capability of the lumtera_report post type maps to lumtera_manage_reports (with map_meta_cap off), so core routes such as XML-RPC's wp.getPost, wp.editPost and wp.deletePost need it too. Administrators always have it. Give it to other roles under Lumtera → Settings → Permissions.

Changing settings, including check severities, always needs manage_options.

php
// Let site managers with a custom capability see the reports.
add_filter( 'lumtera_capability', fn() => 'manage_accessibility' );

Admin screens ​

HookTypeParametersPurpose
lumtera_edit_urlfilterstring $url, int $post_idThe "edit" link Lumtera uses everywhere. Point it at your page builder.
lumtera_admin_groupsfilterarray $groupsThe Lumtera menu's groups, as key => { label, screens, tabs? }. screens lists page slugs in order. Built in: overview, check, fix, report, engage, agency, settings and upgrade (menu only, tabs false). Put your screen in a group here. A screen no group lists gets its own menu item and tab.
lumtera_screen_orderfilterstring[] $orderOrder of Lumtera's screens (page slugs), for the tabs and the menu
lumtera_admin_tabsfilterarray $tabs (slug => label)Screens shown as tabs in the Lumtera header. Add your own screen here, and put it in a group with lumtera_admin_groups.
lumtera_admin_tab_badgesfilterarray $badges (slug => text)Short tags shown after a tab's label, such as "Pro" on screens that need a license
lumtera_admin_upgrade_screensfilterstring[] $screensScreens shown as an upgrade instead of a working screen. Each sorts last in its group, and a group of only these gets a tag in the menu (the screen's lumtera_admin_tab_badges text, or "Pro"). Default: Lumtera's Pro preview screens without Pro, and none with it. Lumtera Pro uses it for screens the active plan doesn't include.
lumtera_settings_sectionsfilterarray $sections (slug => label)Settings sections. See the list below.
lumtera_settings_groupsfilterarray $groupsHow the Settings index groups its sections, as key => { label, sections }. Built in: checking (Checking), fixing (Fixing), notifications (Feedback and notifications), people (People) and pro (Lumtera Pro). A section no group lists goes under "More".
lumtera_settings_metafilterarray $metaEach section's icon and one-line description on the Settings index, as slug => { icon, description }. An unknown icon shows as a dot.
lumtera_settings_keywordsfilterarray $keywords (slug => words)Extra words, separated by commas, that Find a setting matches for each section
lumtera_settings_section_{$slug}actionRenders your own settings section
lumtera_overview_after_statsactionarray $totalsOutput below the Overview summary cards
lumtera_show_getting_startedfilterbool $showWhether the Overview shows the free getting-started checklist. Default true (for people who manage the site, until they dismiss it). Lumtera Pro returns false while its own first-run checklist shows, so there are never two.
lumtera_ask_for_reviewfilterbool $askWhether the Overview may show the review request. Default true; it still waits a week and for real progress. Return false to never ask, for example on client sites.
lumtera_content_actionsactionarray $filtersButtons in the Content report header
lumtera_selectable_post_typesfilterWP_Post_Type[] $typesContent types offered under Lumtera → Settings → General
lumtera_pricing_urlfilterstring $urlThe pricing page that "Upgrade to Pro", plan links and Pro tips open. Lumtera adds its own tracking tags and plan anchor to it.
lumtera_show_upgradefilterbool $showWhether "Upgrade to Pro" buttons, Pro tips and the weekly email's note about Pro show. The Pro preview screens follow whether Lumtera Pro is active, not this filter. Default: true unless Lumtera Pro is active.
lumtera_docs_basefilterstring $baseThe documentation address the Docs buttons use, ending in a slash, for example for a mirror. Default https://lumtera.mantrabrain.com/docs/.
lumtera_docs_pathfilterstring $path, string $slug, string $sectionWhich documentation page an admin screen's Docs button opens. $slug is the screen's page slug and $section the settings section, if any. For Settings, the filter runs only for sections Lumtera doesn't already link (an add-on's own section). An empty path opens the docs home. Lumtera Pro uses it to point its own screens (Test sessions, Fixes queue, Evidence, Compare scans, the conformance report and Fix approvals) at their pages.
lumtera_elementor_preview_assetsactionRuns when Elementor's preview loads

Built-in settings sections ​

SlugLabelPlugin
generalGeneralLumtera
checksChecksLumtera
site-fixesSite fixesLumtera
aiAI suggestionsLumtera
permissionsPermissionsLumtera
feedbackFeedbackLumtera
email-summaryEmail summaryLumtera
ignoreIgnore rulesLumtera Pro
fix-approvalsFix approvalsLumtera Pro
alertsAlertsLumtera Pro
feedback-targetsResponse targetsLumtera Pro
brandingBrandingLumtera Pro
integrationsIntegrationsLumtera Pro
issue-trackersIssue trackersLumtera Pro
licenseLicenseLumtera Pro
php
// Add your own section to the "Feedback and notifications" group.
add_filter( 'lumtera_settings_sections', fn( $sections ) => $sections + [ 'acme' => 'Acme export' ] );
add_filter( 'lumtera_settings_groups', function ( $groups ) {
	$groups['notifications']['sections'][] = 'acme';
	return $groups;
} );
add_action( 'lumtera_settings_section_acme', function () {
	echo '<p>Acme export settings…</p>';
} );

Media Library ​

HookTypeParametersPurpose
lumtera_import_embedded_altfilterbool $importWhether Lumtera copies alt text embedded in an uploaded image's metadata (IPTC) into the Media Library, when the image has none. Default: true only before WordPress 7.0, which does this itself.

Email summary ​

HookTypeParametersPurpose
lumtera_email_summary_activefilterbool $activeWhether the weekly email summary is scheduled and sent. Default: the setting, and off while Lumtera Pro sends its own weekly digest (see the next filter).
lumtera_home_watch_urlfilterstring $urlThe page the weekly home page check loads. Default home_url( '/' ). It must be on this site: other addresses are refused.
lumtera_pro_sends_digestfilterbool $sendsWhether Lumtera Pro's weekly digest replaces the free summary. Default: whether Lumtera Pro is active. Lumtera Pro returns whether its license is active, so with Pro installed but unlicensed, the free summary is still sent.

Agency hub ​

HookTypeParametersPurpose
lumtera_site_summaryfilterarray $dataThe summary an agency hub reads from /lumtera/v1/site-summary and the site-summary ability. Add fields. Don't remove or retype existing ones: older hubs rely on them.

AI ​

HookTypeParametersPurpose
lumtera_ai_alt_text_prefilterarray|WP_Error|null $pre, int $image_id, array $contextReturn alt text suggestions to skip the WordPress AI Client, for example to use another service. Return null to carry on.
lumtera_ai_link_text_prefilterarray|WP_Error|null $pre, array $context, int $post_idThe same, for link text suggestions
lumtera_ai_headings_prefilterarray|WP_Error|null $pre, array $context, int $post_idThe same, for heading suggestions
lumtera_ai_summary_prefilterarray|WP_Error|null $pre, array $context, int $post_idThe same, for plain-language summaries
lumtera_ai_rate_limitfilterint $limit (default 30)AI requests one user may make in 10 minutes, across all AI features. 0 turns the limit off. Lumtera Pro's fixes queue reads the same allowance.

For alt text, $context has post_title, caption, heading, nearby, linked and link_text. Return the same shape the AI Client call produces:

php
add_filter( 'lumtera_ai_alt_text_pre', function ( $pre, int $image_id, array $context ) {
	$suggestion = my_captioning_service( wp_get_attachment_url( $image_id ), $context );

	return [
		'suggestions' => [ $suggestion ],
		'decorative'  => false,
		'note'        => '',
	];
}, 10, 3 );

The writing filters get what would be sent to the provider, and your answer is tidied the same way as the provider's:

Filter$context keysReturn
lumtera_ai_link_text_prelink_text, destination, destination_title, sentence, paragraph, post_title, rule{ suggestions: string[], note }
lumtera_ai_headings_premode (bold or outline), post_title; for bold: text, next, previous; for outline: paragraphsbold: { suggestions: string[], note }. outline: { headings: [ { before, text } ], note }, where before is the 1-based paragraph number.
lumtera_ai_summary_prepost_title, text, words, target{ summary, note }

Return a WP_Error to fail the request with your own message. When one of these filters is in use, Lumtera also offers the matching AI fix without the WordPress AI Client. See AI features.

Site fixes ​

HookTypeParametersPurpose
lumtera_site_fixes_skip_link_targetfilterstring $targetSkip-link target ID (without #) when none is saved
lumtera_site_fixes_theme_has_skip_linkfilterbool $hasWhether the theme already prints a skip link, so Lumtera adds none
lumtera_site_fixes_viewportfilterstring $contentThe viewport tag content printed by the zoom fix. Default: width=device-width, initial-scale=1.
lumtera_site_fixes_filter_contentfilterbool $runWhether the link fixes run on this request
lumtera_purge_page_cacheactionAfter Lumtera asked the page caches it knows to empty, because something every page shows changed. Hook in to empty a cache it doesn't know, such as a host's or a CDN's.
php
// My theme prints its skip link in a way Lumtera can't detect.
add_filter( 'lumtera_site_fixes_theme_has_skip_link', '__return_true' );

Storage ​

See Stored data.

HookTypeParametersPurpose
lumtera_tablesfilterstring[] $tablesFull names of the database tables Lumtera and add-ons need on each site. When one is missing, Lumtera's screens tell administrators and offer a button that recreates the tables.
lumtera_repair_tablesactionWhen an administrator repairs Lumtera's tables. Add-ons recreate theirs.
lumtera_derived_post_metafilterstring[] $keysPost meta keys that hold results for this site only (the summary, the score and the reopened marker), so they're left out of WordPress exports and imports

JavaScript ​

wp.hooks filters ​

These use wp.hooks in the block editor. Load your script with lumtera-editor as a dependency.

HookTypeSignaturePurpose
lumtera.quickFixersfilter( fixers ) => fixersQuick fixes, keyed by check ID. Each is ( block, issue ) => { label, done, apply } | null. See Add a quick fix.
lumtera.issueActionsfilter( actions, issue, { postId, recheck, focusNext } ) => actionsExtra buttons on each issue card. Return an array of elements. recheck() runs the check again, for example after your action changed what's reported. focusNext() re-checks and moves focus to the next issue, as a quick fix does. Use it after your action edited the post. Lumtera's AI writing suggestions and Pro's Track fix and Ignore everywhere… buttons use it.
lumtera.readingActionsfilter( actions, readability, { postId, builder } ) => actionsExtra elements under the reading level. readability is the report's readability object. builder is true when a page builder renders the post. Lumtera's AI summary uses it.
js
wp.hooks.addFilter( 'lumtera.issueActions', 'acme/report', ( actions, issue ) => [
	...actions,
	wp.element.createElement( wp.components.Button, {
		variant: 'link',
		onClick: () => window.open( 'https://example.com/ask?rule=' + issue.rule ),
	}, 'Ask the team' ),
] );

The block editor data store lumtera/checks exposes the current results. Selectors: getReport(), getCounts(), getStatus(), getError(), getCheckedAt(), isHighlighting() and getBlockSeverity( clientId ).

js
const counts = wp.data.select( 'lumtera/checks' ).getCounts();

Globals ​

These are loaded only for signed-in users who may use review mode, never for visitors.

GlobalWhereWhat it is
window.lumteraAuditPages in audit mode and review modeThe browser engine behind the whole-page checks. register( measure ) adds a measure (name, collect( ctx ), decide( data ), and optionally phase, order, budget, onDemand and rules). run( options ) runs the measures and resolves to { version, findings, partial, failed, errors, ms, viewport, measures }. measures() and rules() list what is registered. A lumtera-audit-ready event fires on window once it has loaded.
window.lumteraFixModalThe Content report and the block editorThe Fix without opening dialog, which shows a fix as before and after, applies it and undoes it. open() opens it; supports() says whether a fix can be shown this way.
window.lumteraReviewKeyboardReview modeBuilds the Keyboard tab. create( api ) returns its tab and panel.
window.lumteraReviewSrPreviewReview modeBuilds the Screen reader tab, an approximation of what screen readers announce. create( api ) returns its tab and panel.
window.lumteraReviewManualReview modeBuilds the Manual checks tab from the post's guided checklist. create( api ) returns its tab and panel.

Only lumteraAudit.register() and lumteraAudit.run() are meant for add-ons. The review-mode globals are how Lumtera's own review mode puts its tabs together.

js
// A measure an add-on enqueues on the lumtera_audit_scripts action.
window.lumteraAudit.register( {
	name: 'acme-marquee',
	rules: { 'acme-marquee': { criteria: [ '2.2.2' ], severity: 'warning', confidence: 'likely' } },
	collect: () => ( { count: document.querySelectorAll( 'marquee' ).length } ),
	decide: ( data ) => ( data.count ? [ { rule: 'acme-marquee', data } ] : [] ),
} );

Findings from a measure need a matching MeasuredRule class on the server, added with lumtera_measured_rule_classes, for their wording.

Lumtera Pro ​

These hooks exist only while Lumtera Pro is active. Most Pro features also need an active license.

Plans and license ​

HookTypeParametersPurpose
lumtera_pro_canfilterbool $allowed, string $feature, int $planWhether the plan includes a feature. Only asked while the license is active. Features on every plan: acr, carousel_motion, evidence, form_flows, role_scans, state_contrast. Growth and up: white_label, client_emails, share_links, portfolio, consistency. Agency and up: approval_policy, network. An unknown feature is refused. $plan is the store's price ID (see lumtera_pro_price_plan_map; 0 when unknown).
lumtera_pro_limitfilterint $limit, string $feature, int $planA plan's allowance for a counted feature: scheduled_pages (pages per scheduled run), batch_size (fixes per queue run), portfolio_sites (client sites) or role_scans (roles for signed-in checks). 0 means no set number, and -1 that the plan doesn't include it. A feature that isn't listed gets -1. Defaults below.
lumtera_pro_price_plan_mapfilterarray $map (price ID => plan)Maps the store's price IDs to plans (1 Personal, 2 Growth, 3 Agency, 4 Unlimited). Default: 1–4 are the yearly prices and 5–8 the lifetime prices of the same plans. Entries that aren't a positive price ID mapped to a known plan are ignored, and an ID missing from the map counts as Personal.
lumtera_pro_license_activatedactionint $planAfter a key is activated on this site (or network). Pro uses it to put every scheduled job back in place.
lumtera_pro_license_checkedactionstring $statusAfter each daily license check with the store, with the stored status, such as active, expired or inactive.
lumtera_pro_onboarding_stepsfilterarray $steps, int $planThe steps of the first-run checklist. Each step is { id, title, text, url, action, done }; done ticks it off.
lumtera_pro_scheduled_page_limitfilterint $limit, int $planPages one scheduled check run may fetch, after lumtera_pro_limit. A run always has a cap: 0 from lumtera_pro_limit becomes the top plan's 500. At least 1.
lumtera_pro_license_is_activefilterbool $active, string $statusMaster switch for Pro features. On a network, a license below the Agency plan is already false here for sites other than the main site.
lumtera_pro_network_licensefilterbool $networkWhether the license is stored and activated once for the network. Default: whether Pro is network-activated.
lumtera_pro_license_data_updatedactionstring $key, string $status, array $responseAfter the stored license changes (activate, check, save or deactivate). $status is the stored status (active, expired, inactive…). $response holds the fields Lumtera keeps from the store's answer. On deactivation $key is empty and $status is inactive. Pro's updater uses it to forget its cached update check.
lumtera_pro_license_store_urlfilterstring $urlThe store endpoint that license and update requests go to. Only an https address is used. Otherwise nothing is sent. lumtera_pro_license_store_url_default sets the fallback when no constant sets one. For a staging store only.
lumtera_pro_license_store_url_defaultfilterstring $urlThe store endpoint used when neither LUMTERA_PRO_LICENSE_STORE_URL nor LUMTERA_PRO_STORE_URL is defined.
lumtera_pro_license_item_idfilterint $idThe store's download ID for Lumtera Pro, sent with license and update requests. lumtera_pro_license_item_id_default sets the fallback when no constant sets one. For a staging store only.
lumtera_pro_license_item_id_defaultfilterint $idThe download ID used when neither LUMTERA_PRO_LICENSE_ITEM_ID nor LUMTERA_PRO_ITEM_ID is defined.
lumtera_pro_license_item_namefilterstring $nameThe product name sent with the download ID
lumtera_pro_license_admin_urlfilterstring $urlWhere the Activate license link in the Plugins screen's update row goes. Default: Lumtera → Settings → License.
lumtera_pro_license_upgrade_urlfilterstring $urlWhere the "get a license" link in the update row goes. Default: the store's account page.

Default limits for lumtera_pro_limit:

FeaturePersonalGrowthAgencyUnlimited
scheduled_pages25100250500
batch_size25100250500
portfolio_sites-1 (none)5250 (no set number)
role_scans110 (no set number)0 (no set number)
php
// Let a staging site check more pages per scheduled run.
add_filter( 'lumtera_pro_limit', function ( int $limit, string $feature ) {
	return 'scheduled_pages' === $feature && 'staging' === wp_get_environment_type() ? 1000 : $limit;
}, 10, 2 );

Scheduled and browser page checks ​

HookTypeParametersPurpose
lumtera_pro_scheduled_templatesfilterarray $templates (each {url, title})Key templates scheduled checks include automatically
lumtera_pro_scheduled_urlsfilterarray $urls (each {url, title})The final list of pages for scheduled checks, in order. Only this site's pages are ever requested. The list is capped at the plan's page limit.
lumtera_pro_sitemap_urlsfilterstring[] $listSitemap addresses scheduled checks try, in order. Only this site's addresses are requested.
lumtera_pro_scheduled_page_checkedactionstring $url, string $html, array $rowAfter a scheduled check stored a page. $html is the markup as fetched.
lumtera_pro_browser_page_checkedactionstring $url, string $html, string $viewportAfter a browser check stored a page. $viewport is desktop or phone.
lumtera_pro_ai_review_prefiltermixed $pre, string $kind, array $contextAnswer an AI rule yourself instead of calling the provider. $kind is text or image.
lumtera_pro_statement_languagefilterbool $has, string $localeWhether a language has a translation of the statement's text, so a draft in it can be offered.
lumtera_pro_page_checks_sideactionIn the Page checks screen's side column, after the scheduled checks card. Signed-in checks use it.
lumtera_pro_page_checks_after_resultsactionAfter the results card on the Page checks screen. Form tests use it.
lumtera_pro_send_page_alertfilterbool $send, string $url, string[] $newStop an alert from a scheduled page check. $new holds the new errors' fingerprints.

Form tests, consistency and signed-in checks ​

HookTypeParametersPurpose
lumtera_pro_form_testedactionarray $formAfter a form test's outcome is stored. $form includes its last result.
lumtera_pro_consistency_checkedactionarray $resultAfter cross-page consistency was checked. $result is { checked_at, pages, items }.
lumtera_pro_role_scan_shop_pagesfilterarray $pagesWooCommerce pages signed-in checks can include, as key => { label, url }. Only this site's addresses are kept.
lumtera_pro_role_scan_sample_productfilterint $product_idThe product signed-in checks put in the test user's cart. 0 for none.
lumtera_pro_pass_blocked_query_keysfilterstring[] $keysQuery arguments that make a page do something when opened (add-to-cart, action, _wpnonce and more), so a signed-in check never opens an address carrying one

Alerts and monitoring ​

HookTypeParametersPurpose
lumtera_pro_send_alertfilterbool $send, int $post_id, Issue[] $newStop an alert about new errors on a post. $new is keyed by fingerprint.
lumtera_pro_importingfilterbool $importingTreat this request as an import: record the starting point without alerting. Default: whether WP_IMPORTING is on. Pro turns it on while it re-checks posts after an ignore rule changes.
lumtera_pro_use_action_schedulerfilterbool $use (default true)Use Action Scheduler when it's loaded (for example with WooCommerce), instead of WP-Cron

Fixes queue and evidence ​

HookTypeParametersPurpose
lumtera_pro_remediation_ai_allowedfilterbool $allowedWhether a fixes queue run may ask for another AI draft now. Default: whether the user still has AI requests left under lumtera_ai_rate_limit.
lumtera_pro_ledger_lock_waitfilterint $seconds (default 2)Seconds a new evidence entry waits for the ledger lock before it's stored outside the chain

Events, reports and clients ​

HookTypeParametersPurpose
lumtera_pro_eventactionstring $event, array $payloadFires for every Pro event. See the event list.
lumtera_pro_event_payloadfilterarray $payloadChange an event payload before it's logged and sent
lumtera_pro_reports_sectionsactionOutput below the report list on the Reports screen. Pro's conformance report card uses it.
lumtera_pro_report_row_actionsactionint $report_id, string $nameAmong a report's actions in the reports list (for example Share). $name is the title and client, for screen reader labels.
lumtera_pro_burden_savedactionint $id, array $record, array|null $oldAfter a burden record is saved. $old is null for a new record.
lumtera_pro_client_rest_allowedfilterstring[] $allowed (default empty)REST routes the read-only client role may still write to, for an add-on's read-style POST route, for example /my-addon/v1/preview
lumtera_pro_portfolio_safe_httpfilterbool $safe (default true)Use WordPress's safe HTTP client for portfolio connections. Turn off only for local development.

Lumtera Pro is built on the free plugin's hooks, including lumtera_ignored_issue (ignore rules, and on whole pages), lumtera_page_result_summaries, lumtera_page_result_load and lumtera_page_result_sources, lumtera_page_features, lumtera_measured_rule_classes, lumtera_audit_scripts, lumtera_signed_check_request, lumtera_change_allowed, lumtera_statement_burden_items, lumtera_feedback_views, lumtera_feedback_badges, lumtera_can_check_all, lumtera_permission_defaults, lumtera_pro_sends_digest, lumtera_docs_path, lumtera_admin_upgrade_screens, lumtera_show_getting_started, lumtera_tables and the settings filters (lumtera_settings_sections, _groups, _meta, _keywords).

php
// Don't alert about posts in the "Archive" category.
add_filter( 'lumtera_pro_send_alert', function ( bool $send, int $post_id ) {
	return $send && ! has_category( 'archive', $post_id );
}, 10, 2 );

// Always check the pricing page on schedule.
add_filter( 'lumtera_pro_scheduled_urls', function ( array $urls ) {
	array_unshift( $urls, [ 'url' => home_url( '/pricing/' ), 'title' => 'Pricing' ] );
	return $urls;
} );

// Forward every Pro event to your own logger.
add_action( 'lumtera_pro_event', function ( string $event, array $payload ) {
	error_log( $event . ' ' . wp_json_encode( $payload['object'] ) );
}, 10, 2 );

Constants ​

Pro Every plan

Set these in wp-config.php:

ConstantEffect
LUMTERA_PRO_ENCRYPTION_KEYKey used to encrypt portfolio passwords, webhook secrets and issue tracker tokens. Set it if your salts change, for example when a host rotates them.
LUMTERA_PRO_DELETE_REPORTStrue deletes reports, fix history, page results and the activity log on uninstall
LUMTERA_PRO_DELETE_LICENSEtrue deletes the license on uninstall, and first frees this site's activation on the store
LUMTERA_PRO_LICENSE_STORE_URLThe store endpoint for license and update requests, instead of the default. Must be https. (LUMTERA_PRO_STORE_URL also works.) For a staging store only.
LUMTERA_PRO_LICENSE_ITEM_IDThe store's download ID for Lumtera Pro, instead of the default. (LUMTERA_PRO_ITEM_ID also works.) For a staging store only.