Skip to content

REST & WP integration

Ultimate Watermark does not register custom REST routes. There are no /wp-json/ultimate-watermark/v1/... endpoints. There's also no shortcode, no Gutenberg block, no Elementor widget, and no WP-CLI command in the current build.

So how does it apply watermarks to images uploaded via the REST API or block editor?

By hooking WordPress core's media pipeline at multiple points. This page documents every hook used, in the order they fire, so you can plan integrations around them.

The integration points

When an image is uploaded — whether via the classic Media Library, the block editor, the REST /wp-json/wp/v2/media endpoint, or a custom plugin — WordPress fires a series of hooks. Ultimate Watermark listens to several:

1. wp_handle_upload (per-file, on classic upload)

MediaLibraryIntegration.php:34–35 listens here. Triggered by wp_handle_upload when admin uploads through the standard form.

The plugin checks the new file's MIME type and, if eligible, applies watermarks after WordPress writes the file to disk.

2. wp_generate_attachment_metadata (per-attachment, all upload paths)

RestApiIntegration.php:60 filters this hook. WordPress fires it whenever it generates the metadata array for an attachment — which happens for every upload path: classic, block editor, REST, WP-CLI, custom plugins.

Per-subsize watermarking happens here. For each subsize (thumbnail, medium, large, full, etc.), the watermark is painted into the file on disk if rules match.

3. add_attachment and rest_after_insert_attachment

RestApiIntegration.php:20–61 hooks both. Fires after the new attachment post is created. Useful for late-binding metadata that depends on the post (e.g. featured-image-aware rules).

4. rest_request_before_callbacks

Fires before any REST callback — used to set up runtime state for REST-driven uploads.

5. rest_insert_attachment

Fires when an attachment is inserted via REST.

When a post sets a featured image (_thumbnail_id), Ultimate Watermark loops through the gallery list and applies watermarks where rules match (RestApiIntegration.php:226–270).

What hooks each upload type uses

Upload pathHooks observedSubsizes watermarked
Media → Add New (classic)wp_handle_upload + wp_generate_attachment_metadataAll matching rules
Block editor (insert image)wp_generate_attachment_metadataAll matching rules
REST POST /wp/v2/mediawp_generate_attachment_metadata + rest_insert_attachment + rest_after_insert_attachmentAll matching rules
Programmatic wp_insert_attachmentwp_generate_attachment_metadataAll matching rules
WP-CLI wp media importSame as programmaticAll matching rules

Bottom line: every upload path goes through wp_generate_attachment_metadata, so watermarks always apply.

What's NOT integrated automatically

  • Featured image already set before plugin activation — won't be watermarked retroactively. Use Bulk → Apply in Media Library.
  • Direct file uploads outside WP: SCP, FTP, etc. — bypass the metadata pipeline. Use Bulk → Apply afterward.
  • Custom upload plugins that skip wp_generate_attachment_metadata — won't trigger watermarking.

OTF (Pro) frontend integration

Pro's on-the-fly module hooks differently. It filters HTML output to swap original <img> URLs for cached watermarked URLs:

HookWhat it does
the_contentRewrites image URLs in post bodies.
post_thumbnail_htmlRewrites featured-image markup.
wp_get_attachment_image_attributesTweaks attachment image rendering.
wp_calculate_image_srcsetStrips srcset so only the OTF-rendered image is served.
WooCommerce-specific hooksWhen otf_apply_to_woocommerce is on.

OTF's URL hash includes the original file mtime, so cache invalidation is automatic.

A "real" REST API for Ultimate Watermark

If you need a true /wp-json/ultimate-watermark/v1/... endpoint to drive watermarking from outside WordPress, you can add one yourself in a small mu-plugin:

php
add_action( 'rest_api_init', function () {
    register_rest_route( 'ultimate-watermark/v1', '/apply', [
        'methods'             => 'POST',
        'callback'            => function ( $request ) {
            $attachment_id = (int) $request['attachment_id'];
            // Re-trigger metadata generation, which fires watermarking.
            $file = get_attached_file( $attachment_id );
            $meta = wp_generate_attachment_metadata( $attachment_id, $file );
            wp_update_attachment_metadata( $attachment_id, $meta );
            return rest_ensure_response( [ 'ok' => true ] );
        },
        'permission_callback' => function () {
            return current_user_can( 'manage_options' );
        },
    ] );
} );

This is the same flow the bulk action uses internally.

Triggering a re-apply programmatically

Inside a plugin or theme:

php
$attachment_id = 123;
$file = get_attached_file( $attachment_id );

// Regenerate metadata (re-runs subsize creation + watermarking).
$meta = wp_generate_attachment_metadata( $attachment_id, $file );
wp_update_attachment_metadata( $attachment_id, $meta );

Or use the popular Regenerate Thumbnails plugin — it fires the same hook.

Restoring an unwatermarked original

If backups are enabled and a backup exists for the file:

php
$attachment_id = 123;
do_action( 'ultimate_watermark_restore_original', $attachment_id );

The restore action requires backups to have been enabled at the time of the original watermark. See Settings → Backups.

REST authentication

Standard WordPress REST authentication applies — cookies + nonce for the same site, Application Passwords or JWT for off-site clients. Ultimate Watermark doesn't add any auth layer of its own.

What about WP-CLI?

The plugin doesn't ship WP_CLI::add_command registrations. Useful CLI workflows still work via existing WP commands:

bash
# Re-watermark every image (regenerates subsizes + fires our hooks).
wp media regenerate --yes

# Re-watermark just one attachment.
wp media regenerate 123 --yes

# Process in chunks to avoid memory issues.
wp media regenerate $(wp post list --post_type=attachment --posts_per_page=50 --offset=0 --field=ID) --yes

Webhooks

Ultimate Watermark doesn't expose webhooks of its own. To send a notification on each watermark:

php
add_action( 'ultimate_watermark_saved', function ( $id, $data ) {
    wp_remote_post( 'https://your-webhook.example.com/uw-saved', [
        'body' => wp_json_encode( [ 'id' => $id, 'data' => $data ] ),
    ] );
}, 10, 2 );

Where to go next