Developer guide · last reviewed August 2026
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 appenddo_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.
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:
| Attribute | Values | Default |
|---|---|---|
type | ai-generated, ai-edited, ai-assisted, chatbot | ai-generated |
style | inline, badge, banner | inline |
text | Your own sentence, replacing the default for that type | per type |
bg / color | Hex colours, e.g. #0e1512 | theme default |
mark | aim, eu, none | aim |
logo | An image URL, shown beside the mark | none |
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.
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.
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.
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.
Things that will bite you
- Priority.
add_filterdefaults 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_singularguard 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, hookthe_content_feedseparately 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.
Frequently asked questions
Where should this code live?
functions.php is fine too. Never a parent theme, because the next update overwrites it.Is do_shortcode slow?
Can I filter the plugin’s own output instead?
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?
Related guides
Each one stands on its own, in plain English, with the exact steps for a WordPress site.
For developersStyle 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
The lawThe 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
The how-toHow 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
The metadataWhat 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.
The shopDisclose 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
Written textHow 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
ChatbotsHow 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
LanguagesYour 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
The releaseWhat 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