Article 50 is in force since 2 August 2026. Get the free plugin
Guide

Show an AI disclosure automatically, with hooks

Placing a shortcode by hand is fine for one page. For a whole category, a post type, or every product in a shop, you want WordPress to do it. One filter, one condition, one call to do_shortcode, and the notice appears wherever your own rule says it should.

A not-human joining two wooden panels with a single glowing mint cord in a sunlit workshop

Developer guide · last reviewed August 2026

01Summary3 points

The short version

  • AIM Transparency gives you two shortcodes. Anywhere WordPress runs shortcodes, you can place a disclosure by hand.
  • To do it for many posts at once, hook the_content, test your own condition, and append do_shortcode( '[aicl_disclosure]' ). That is the whole pattern.
  • Everything here is the free plugin. Put the code in a small plugin or a child theme, never in a parent theme that updates.

02The shortcodesThree of them

What you have to work with

[aicl_disclosure] is the general one. It states that a piece of content was made with AI, and it takes every option you are likely to want:

AttributeValuesDefault
typeai-generated, ai-edited, ai-assisted, chatbotai-generated
styleinline, badge, bannerinline
textYour own sentence, replacing the default for that typeper type
bg / colorHex colours, e.g. #0e1512theme default
markaim, eu, noneaim
logoAn image URL, shown beside the marknone

Note the hyphens. The type is ai-assisted, not assisted. An unrecognised type quietly falls back to ai-generated, which is a stronger claim than you probably meant, so it is worth getting right.

[aicl_text_disclosure] is the third, and the one to reach for when the line belongs somewhere the automatic placement cannot get to — under a byline rather than above the body, say, or inside a page-builder layout. It takes a single id attribute, defaulting to the current post.

It obeys the record, not the shortcode. Placing it prints nothing unless that post actually owes a disclosure, and placing it by hand suppresses the automatic copy for that post, so the two can never both appear. A shortcode that rendered regardless would let a page claim its text was AI-written when the record says otherwise, which is the one thing this plugin will not do. Set the post’s source type in AI content disclosure first; the shortcode only decides where the line goes, never whether.

[aicl_ai_notice] is the narrow one: the Article 50(1) notice that tells a visitor they are talking to a machine. It takes a single text attribute and otherwise uses whatever you set in the plugin’s settings, so the wording stays in one place.

Which duty are you meeting? [aicl_disclosure] covers AI content you publish. [aicl_ai_notice] covers AI your visitors interact with. They are separate obligations and both start 2 August 2026. The chatbot guide covers the second one properly.

03The patternOne filter

One filter, one condition

Say you tag posts that were drafted with AI help into a category called AI assisted. You want a banner under every one of them, and nothing anywhere else.

add_filter( 'the_content', 'mysite_ai_disclosure' );

/**
 * Append the AI disclosure to posts in the "ai-assisted" category.
 *
 * @param string $content The post content.
 * @return string The content, with a disclosure appended where it applies.
 */
function mysite_ai_disclosure( $content ) {
	if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
		return $content;
	}

	if ( ! has_category( 'ai-assisted' ) ) {
		return $content;
	}

	return $content . do_shortcode( '[aicl_disclosure type="ai-assisted" style="banner"]' );
}

Drop this in a one-file plugin, or your child theme’s functions.php.

Three lines of that are guards, and they matter more than the rest. the_content runs in places you are not thinking about: archive pages, search results, related-post widgets, RSS feeds, and any plugin that renders a post excerpt behind the scenes. Without the guards you get the disclosure repeated down a category page, or stamped into a feed where it makes no sense.

  • is_singular( 'post' ) keeps it to a single post view.
  • in_the_loop() keeps it off content rendered outside the main loop.
  • is_main_query() keeps it off secondary queries a widget or block may be running.

Everything else is your rule. Swap has_category for whatever actually marks your AI content.

04RecipesFour conditions

Four conditions worth copying

A custom field

More precise than a category, because it does not show up in your public taxonomy. Set _ai_disclosure to generated, edited or assisted on each post.

function mysite_ai_disclosure_field( $content ) {
	if ( ! is_singular() || ! in_the_loop() || ! is_main_query() ) {
		return $content;
	}

	$kind = get_post_meta( get_the_ID(), '_ai_disclosure', true );

	if ( ! in_array( $kind, array( 'generated', 'edited', 'assisted' ), true ) ) {
		return $content;
	}

	return $content . do_shortcode( '[aicl_disclosure type="ai-' . $kind . '" style="banner"]' );
}
add_filter( 'the_content', 'mysite_ai_disclosure_field' );

The allow-list is doing real work: it stops a stray meta value becoming an attribute.

A whole post type

If one post type is entirely AI-produced, say a generated glossary, there is no per-post decision to make.

function mysite_ai_disclosure_cpt( $content ) {
	if ( ! is_singular( 'glossary' ) || ! in_the_loop() || ! is_main_query() ) {
		return $content;
	}

	return do_shortcode( '[aicl_disclosure type="ai-generated" style="banner"]' ) . $content;
}
add_filter( 'the_content', 'mysite_ai_disclosure_cpt' );

Prepended rather than appended. For wholly generated content, the disclosure belongs before the reader starts, not after.

WooCommerce product descriptions

Product images need nothing from you: the plugin badges every product image WordPress renders, including the single-product gallery. This is only about the written copy, and it is the one recipe where your theme changes the answer.

On a block theme, which is what WordPress ships by default now, the product page is assembled from blocks and the_content never runs. Filter the block that renders the description instead:

function mysite_ai_disclosure_product( $block_content, $block ) {
	if ( empty( $block['blockName'] ) || 'woocommerce/product-details' !== $block['blockName'] ) {
		return $block_content;
	}

	$id = get_queried_object_id();

	if ( ! $id || ! has_term( 'ai-copy', 'product_tag', $id ) ) {
		return $block_content;
	}

	return $block_content . do_shortcode( '[aicl_disclosure type="ai-assisted" style="inline"]' );
}
add_filter( 'render_block', 'mysite_ai_disclosure_product', 10, 2 );

Tested on Twenty Twenty-Five with WooCommerce 10.9: renders once on a tagged product, and nowhere else.

Note get_queried_object_id() rather than get_the_ID(). Block themes render related products through query loops that are still inside the main query, so in_the_loop() and is_main_query() are both true for a neighbouring product. Pinning to the queried object is what keeps the disclosure on the product the visitor is actually looking at.

On a classic theme, the short-description template filter is the right hook:

function mysite_ai_disclosure_product_classic( $description ) {
	if ( ! is_product() ) {
		return $description;
	}

	if ( ! has_term( 'ai-copy', 'product_tag', get_queried_object_id() ) ) {
		return $description;
	}

	return $description . do_shortcode( '[aicl_disclosure type="ai-assisted" style="inline"]' );
}
add_filter( 'woocommerce_short_description', 'mysite_ai_disclosure_product_classic' );

Do not use this one on a block theme. WooCommerce also applies woocommerce_short_description inside wc_format_content(), which the Store API uses. On a block theme the notice never appears on the page, but your markup does get injected into the JSON the blocks and the API read. Nothing is visible, and something is wrong. See the WooCommerce guide for the image side, which needs none of this.

A site-wide chatbot notice

If you run a chat widget, the notice belongs where the conversation starts, not at the bottom of an article. Rendering it into the footer puts it on every page the widget can appear on.

function mysite_ai_chat_notice() {
	echo do_shortcode( '[aicl_ai_notice]' );
}
add_action( 'wp_footer', 'mysite_ai_chat_notice' );

Then position it next to your widget with a little CSS. The plugin can also inject this for you from Disclosure › Chatbot, which is simpler if you do not need custom placement.

05StylingFrom PHP

Making it look like your site

Every visual option is an attribute, so you can set it from the same code that decides where the notice goes.

$disclosure = sprintf(
	'[aicl_disclosure type="ai-generated" style="banner" bg="%s" color="%s" mark="eu"]',
	esc_attr( get_theme_mod( 'ai_notice_bg', '#0e1512' ) ),
	esc_attr( get_theme_mod( 'ai_notice_fg', '#ffffff' ) )
);

return $content . do_shortcode( $disclosure );

Two behaviours are worth knowing before you spend time on colours.

Contrast is enforced, not suggested. If the pair you pass falls below 4.5:1, the plugin drops both values and renders its default instead. That is deliberate. A disclosure nobody can read is not a disclosure, and the one thing this plugin will not let you do is make it disappear. The Shortcodes panel in the dashboard shows the live ratio as you pick, which is easier than guessing.

The mark is not decoration. mark="eu" uses the European Commission’s official AI-content icon, and it always uses the square one, sized honestly. mark="none" drops it entirely, and logo puts your own image beside the mark rather than instead of it. One exception: a chatbot notice always falls back to the AIM glyph, because the EU set labels AI-generated media and has no icon for the interaction duty. Borrowing one would say the wrong thing.

06Watch outFour traps

Things that will bite you

  • Priority. add_filter defaults to priority 10. If another plugin appends related posts or a share bar at a higher number, your disclosure ends up above theirs. Pass a higher priority if you want it last: add_filter( 'the_content', 'mysite_ai_disclosure', 20 ).
  • Excerpts. If your theme builds excerpts from filtered content, a banner can leak into listing pages. The is_singular guard covers the common case; if you use a page builder, check a category page before you call it done.
  • Feeds. is_singular() is false in a feed, so the code above never fires there. If you decide you do want the disclosure in RSS, hook the_content_feed separately with plain text rather than a styled banner.
  • Do not hide it conditionally. It is tempting to skip the notice for logged-in users, or on a landing page that is converting well. The obligation does not have those exceptions, and a disclosure that appears only sometimes is worse than none, because it makes the pages without it look verified.

07Questions4 answered

Frequently asked questions

Where should this code live?
A small single-file plugin is best: it survives theme changes, and you can switch it off without editing anything. A child theme’s functions.php is fine too. Never a parent theme, because the next update overwrites it.
Is do_shortcode slow?
Not at this scale. It parses one short string and calls one function. The disclosure’s stylesheet only loads on pages where a notice actually renders, so a page without one costs nothing at all. If you were calling it inside a loop over hundreds of posts you would want to think about it; for one notice per page, no.
Can I filter the plugin’s own output instead?
Not yet, and that is on purpose. The shortcode already takes text, bg, color, mark and logo, which covers what a filter would be used for, and it does so without creating a way to blank the disclosure out. Hooks for developers are planned, but anything that could suppress a disclosure entirely is not on that list.
Does this replace labelling my images?
No. These shortcodes disclose written content and interactions. Images are handled separately, and better: flag one in the Media Library and the plugin adds the visible badge, writes IPTC/XMP provenance into the file itself, and outputs schema.org JSON-LD. Start with labelling your images.

08Keep reading9 guides

Each one stands on its own, in plain English, with the exact steps for a WordPress site.

A not-human in a sunlit workshop shaping a small glowing mint label, four finished label shapes on the benchFor developers

Style the AI badge and disclosures with CSS

Every class name, custom property and data attribute the plugin puts on your page — and the specificity trap that makes correct-looking CSS do nothing.

Read guide
A studio assistant reviewing a row of framed prints, some marked with an AI badgeThe law

The EU AI Act and WordPress (Article 50)

Who Article 50 covers, the 2 August 2026 deadline, the penalties, and exactly what a WordPress site owner must do, in plain English.

Read guide
A studio assistant placing AI-disclosure badges on a wall of framed imagesThe how-to

How to label AI images in WordPress

A step-by-step walkthrough: install the plugin, flag an image from the Media Library, and get the badge, embedded metadata and JSON-LD automatically.

Read guide
A studio assistant pressing a provenance seal onto a photo print beside a card catalogueThe metadata

What IPTC DigitalSourceType actually is

The machine-readable tag behind “AI-generated”: what the IPTC DigitalSourceType vocabulary means, the values AIM Transparency writes for AI and human provenance alike, and why it future-proofs your images.

Read guide
A shop assistant tagging AI-disclosed products in a boutique, with a badged product photo on the counterThe shop

Disclose AI product images in WooCommerce.

Selling with AI product photos? Put the “AI Generated” badge across your shop grid, your categories and the single-product gallery, free, plus embedded provenance, on any theme. Compliance and buyer trust, in one move.

Read guide
A not-human laying a small mint disclosure strip along the top edge of a page of writingWritten text

How to disclose AI-written text in WordPress

The half of Article 50(4) nobody quotes. Whether the text duty reaches your site at all, how to record it per post, and why human review on its own is not the exemption.

Read guide
A not-human attaching a small glowing label to the corner of a chat windowChatbots

How to disclose an AI chatbot on WordPress

Article 50(1) is the other half of the law: if visitors interact with an AI, you have to tell them. Where the notice belongs, and how to find widgets already running.

Read guide
Five different not-human characters each tagging their own framed photographLanguages

Your AI disclosure has to be in a language visitors read

Article 50 asks you to inform the person. An English badge on a Polish shop does not. How disclosure works across 22 EU languages, and where the wording comes from.

Read guide
A not-human in a sunlit workshop feeding a photograph through a hand press, with three progressively smaller copies emerging, each already carrying a small glowing mint sealThe release

What WordPress 7.1 changes for AI image provenance

7.1 can rebuild your images in the browser before they ever reach your server. What that does to provenance written inside the file, what we measured against the release candidate, and the one line that turns it off.

Read guide

Article 50 is in force.

Install the free plugin, label what is AI, and have the record to show for it.

Get the free plugin