Skip to main content
SocialCutter

CMS and websites

Integrate SocialCutter with WordPress and WooCommerce

Upload the master to WordPress over REST, generate every size with SocialCutter and set the right output as featured image. WooCommerce variant and PHP snippet.

  • WordPress
  • WooCommerce
  • REST API
  • Application Passwords
  • featured image
  • media library
  • PHP

Why a single master image

The flow is: you upload one master, SocialCutter returns one output per destination, and WordPress receives the right one in each slot. The default cover fit mode is a centred crop: it scales the image and trims the excess evenly on both sides.

Slot in WordPressSocialCutter destinationSize
Post featured imagelinkedin post1200x627 (1.91:1)
Image inside the contentfacebook post1200x630 (1.91:1)
Social card for the posttwitter post1200x675 (16:9)
Theme header bannertwitter header1500x500 (3:1)
WooCommerce product (main)instagram post1080x1080 (1:1)
Second product imageinstagram story1080x1920 (9:16)

Before you start

  • WordPress 5.6 or newer with HTTPS enabled (Application Passwords arrived in 5.6).
  • A SocialCutter key created in the dashboard under Profile → API keys. It starts with sc_ and is shown only once.
  • For WooCommerce, REST API keys generated in WooCommerce → Settings → Advanced → REST API.
export WP_URL="https://your-blog.com"
export WP_USER="username"
export WP_APP_PASSWORD="abcd efgh ijkl mnop qrst uvwx"
export SC_KEY="sc_your_key"

1. Create an Application Password

In wp-admin go to Users → Profile → Application Passwords, give it a name such as SocialCutter media bot and copy the value: it is shown once.

It travels over HTTP Basic Auth (RFC 7617) on HTTPS; with curl, --user builds the base64 header for you:

curl -s --user "$WP_USER:$WP_APP_PASSWORD" "$WP_URL/wp-json/wp/v2/users/me" | jq '{id, name}'

Reference: https://developer.wordpress.org/advanced-administration/security/application-passwords/

2. Process the master with SocialCutter

curl -s -X POST "https://api.socialcutter.theboomer.dev/api/v1/images/process" \
  -H "X-API-Key: *** \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "type": "url", "value": "https://your-cdn.com/master.jpg" },
    "destinations": [
      { "platform": "linkedin", "format": "post" },
      { "platform": "instagram", "format": "post" }
    ]
  }' > sc.json

jq '.image_id, (.outputs[] | {url, platform, format})' sc.json

The body is source plus destinations, and the response carries image_id with one URL per destination. For a local file use POST /api/v1/images/process/upload (multipart, 5 MB max).

3. Upload the output to the media library

The media endpoint takes the file in the request body as binary data, plus a Content-Disposition header with the filename:

curl -s -o cover.jpg "$(jq -r '.outputs[0].url' sc.json)"

curl -s -X POST "$WP_URL/wp-json/wp/v2/media" \
  --user "$WP_USER:$WP_APP_PASSWORD" \
  -H "Content-Disposition: attachment; filename=cover-1200x627.jpg" \
  -H "Content-Type: image/jpeg" \
  --data-binary "@cover.jpg" > media.json

jq '{id, source_url, media_type}' media.json

The response carries the attachment id and its source_url. alt_text goes in the same request or a later POST to /wp-json/wp/v2/media/{id}. Reference: https://developer.wordpress.org/rest-api/reference/media/

While uploading the attachment you can also link it to the post through the media item’s own post field, defined as The ID for the associated post of the attachment. It makes the library show the related post without touching the post itself.

The featured image is set by identifier, through the featured_media field, which the REST API reference defines as The ID of the featured media for the post. That field is accepted both when creating the post (POST /wp-json/wp/v2/posts) and when updating it (POST /wp-json/wp/v2/posts/{id}).

When creating the post, the attachment goes in the same request:

MEDIA_ID=$(jq -r '.id' media.json)

curl -s -X POST "$WP_URL/wp-json/wp/v2/posts" \
  --user "$WP_USER:$WP_APP_PASSWORD" \
  -H "Content-Type: application/json" \
  -d "{
    \"title\": \"Sample post\",
    \"status\": \"draft\",
    \"featured_media\": $MEDIA_ID
  }" | jq '{id, status, featured_media}'

If the post already exists, update it with the same field:

curl -s -X POST "$WP_URL/wp-json/wp/v2/posts/42" \
  --user "$WP_USER:$WP_APP_PASSWORD" \
  -H "Content-Type: application/json" \
  -d "{\"featured_media\": $MEDIA_ID}" | jq '{id, featured_media, link}'

What travels in featured_media is the attachment ID, never a URL. To verify the result without downloading the whole post, request only that field with the global _fields parameter:

curl -s --user "$WP_USER:$WP_APP_PASSWORD" \
  "$WP_URL/wp-json/wp/v2/posts/42?_fields=id,featured_media" | jq

Inside the content, use the attachment source_url in a plain img tag so the theme keeps control of its classes and srcset:

<figure>
  <img src="https://your-blog.com/wp-content/uploads/2026/09/cover-1200x627.jpg"
       alt="Real description of the image" width="1200" height="627">
</figure>

Reference: https://developer.wordpress.org/rest-api/reference/posts/

5. Regenerate WordPress intermediate sizes

WordPress does not keep a single copy of each image: when you upload an attachment it generates several intermediate sizes (thumbnail, medium, medium_large, large and whatever the theme registers with add_image_size). Two details of add_image_size( $name, $width, $height, $crop ) matter here:

  • The names thumb, thumbnail, medium, medium_large, large and post-thumbnail are reserved.
  • With $crop = true the crop is a hard, centred crop: the default position is center (or array( 'left', 'top' ) to move it).

The catch: sizes are generated when the file is uploaded. If the theme changes its dimensions later, already-uploaded images keep the old crops and the theme starts asking for files that do not exist. The three typical cases are: a new size was added, you changed the dimensions of an existing one under Settings → Media, or you switched to a theme that uses featured images at a different size.

To find out what is missing, request the attachment in edit context. The media schema exposes the missing_image_sizes field (List of the missing image sizes of the attachment) and the size tree in media_details:

curl -s --user "$WP_USER:$WP_APP_PASSWORD" \
  "$WP_URL/wp-json/wp/v2/media/$MEDIA_ID?context=edit" \
  | jq '{id, missing_image_sizes, sizes: (.media_details.sizes | keys)}'

With WP-CLI

wp media regenerate regenerates the thumbnails of one or more attachments:

# A single attachment
wp media regenerate 123

# A range of IDs
seq 1000 2000 | xargs wp media regenerate

# Only attachments that are missing sizes
wp media regenerate --only-missing

# A single size, without confirmation
wp media regenerate --image_size=large --yes

# The whole library
wp media regenerate --yes

Other options on the command: --skip-delete (keeps old files) and --delete-unknown (deletes files for sizes that are no longer registered). Reference: https://developer.wordpress.org/cli/commands/media/regenerate/

With the Regenerate Thumbnails plugin

If you have no terminal access to the server, the Regenerate Thumbnails plugin does the same from the dashboard, under Tools → Regenerate Thumbnails, and also from the media list and each attachment’s edit screen. It can additionally delete old, unused thumbnail files to free server space. The plugin page warns that it has not been tested with the last three major WordPress releases, and its own author recommends WP-CLI because it is faster since it skips the HTTP overhead. Reference: https://wordpress.org/plugins/regenerate-thumbnails/

Where SocialCutter fits

Both methods above crop from the file that is already in the library. SocialCutter does it before: it produces the file already at the destination’s proportion (instagram post at 1080x1080, linkedin post at 1200x627) and that file enters the library with the correct proportion. The cover crop, the default mode, is a centred crop, the same idea as the hard crop in add_image_size.

That means the intermediate size the theme requests is generated from an original that already has the right proportion, so WordPress’s crop starts from a correct source instead of an image with a different ratio. And if the theme changes its dimensions later you still need wp media regenerate: the benefit is that your network crops are regenerated with SocialCutter and uploaded again, independent of whatever crop the theme made.

WooCommerce exposes its own REST API under /wp-json/wc/v3/. A product has an images field that is a list: the first entry is the main image and the rest form the gallery. Each entry accepts id (an existing media attachment) or src (a URL WooCommerce downloads).

Upload the attachment to the WordPress media endpoint first and reference it by id, so the file is not duplicated:

GALLERY_ID=$(jq -r '.id' media.json)

curl -s -X PUT "$WP_URL/wp-json/wc/v3/products/99" \
  --user "$WC_CK:$WC_CS" \
  -H "Content-Type: application/json" \
  -d "{\"images\": [{\"id\": $GALLERY_ID, \"alt\": \"T-shirt, front view\"}]}" | jq '.id, .images'

It authenticates with the ck_ and cs_ keys over Basic Auth on HTTPS. Docs: https://developer.woocommerce.com/docs/apis/rest-api/v3/products/ — field names can change between releases, so check your version.

7. PHP snippet with wp_remote_post

wp_remote_post is WordPress’s standard helper for external APIs. Paste this into a child theme’s functions.php or ship it as a small plugin.

<?php
/**
 * Plugin Name: SocialCutter Media
 * Description: Processes a master with SocialCutter and sets it as the featured image.
 * Version: 1.0.0
 */

function sc_process_master( int $post_id, string $master_url ): int|WP_Error {
    $key = defined( 'SOCIALCUTTER_API_KEY' ) ? SOCIALCUTTER_API_KEY : '';
    if ( '' === $key ) {
        return new WP_Error( 'sc_no_key', 'SOCIALCUTTER_API_KEY is missing' );
    }

    $response = wp_remote_post( 'https://api.socialcutter.theboomer.dev/api/v1/images/process', array(
        'timeout' => 30,
        'headers' => array( 'X-API-Key' => $key, 'Content-Type' => 'application/json' ),
        'body'    => wp_json_encode( array(
            'source'       => array( 'type' => 'url', 'value' => $master_url ),
            'destinations' => array( array( 'platform' => 'linkedin', 'format' => 'post' ) ),
        ) ),
    ) );

    if ( is_wp_error( $response ) ) {
        return $response;
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    $url  = $data['outputs'][0]['url'] ?? '';

    require_once ABSPATH . 'wp-admin/includes/media.php';
    require_once ABSPATH . 'wp-admin/includes/file.php';
    require_once ABSPATH . 'wp-admin/includes/image.php';

    $attachment_id = media_sideload_image( $url, $post_id, null, 'id' );
    if ( is_wp_error( $attachment_id ) ) {
        return $attachment_id;
    }

    set_post_thumbnail( $post_id, $attachment_id );

    return (int) $attachment_id;
}

media_sideload_image downloads the URL into the library and returns the attachment ID when the fourth argument is 'id'; set_post_thumbnail makes it the featured image. Keep the key in wp-config.php with define( 'SOCIALCUTTER_API_KEY', 'sc_...' );, never inside the theme.

8. No code: Zapier, Make and n8n

An automation tool does the same with nodes: a trigger, an HTTP node calling /api/v1/images/process and a WordPress or WooCommerce node that uploads the media and assigns it. The full flow is in the n8n automation guide; Zapier and Make follow the same pattern.

Cost

  • 1 use per destination (platform and format) per request; duplicates are not charged twice.
  • Failed processings are refunded.
  • Every plan includes API and MCP: Free 3 uses/day, Basic 10, Pro 30, Agency 100.

Common errors

SymptomCauseFix
401 from WordPressMistyped application password, or a site without HTTPSRegenerate the password and check TLS
401 from SocialCutterMissing, malformed or revoked keySend X-API-Key with an active sc_ key
413 from SocialCutterThe master is over 5 MBShrink the image before uploading it
The featured image does not changeA URL was sent in featured_mediaSend the attachment ID, not the URL
400 creating the postThe featured_media ID does not exist or points at a deleted attachmentCheck the id with GET /wp-json/wp/v2/media/{id}
The theme asks for a thumbnail that does not existThe theme registered that size after the image was uploadedRegenerate with wp media regenerate --only-missing or the Regenerate Thumbnails plugin

Next steps

Frequently asked questions

Do I need a plugin to call SocialCutter from WordPress?

No. WordPress ships wp_remote_post, which talks to any REST API. That function plus a few lines in functions.php or in your own plugin is enough.

How do I authenticate against the WordPress REST API?

With Application Passwords, available since WordPress 5.6. Create one in Users → Profile → Application Passwords and send it over HTTPS using HTTP Basic Auth. Never use the account's main password.

Is the featured image set by URL or by identifier?

By identifier. The featured_media field on a post expects the attachment ID returned by the media library, not a URL.

Can I set the featured image while creating the post?

Yes. The featured_media field is accepted both when creating a post with POST /wp-json/wp/v2/posts and when updating one with POST to /wp-json/wp/v2/posts/{id}. In both cases the value is the attachment ID.

How do I check that the featured image was set?

Request only that field with the global _fields parameter: GET /wp-json/wp/v2/posts/42?_fields=id,featured_media returns the assigned attachment ID. If it is still 0, the value sent was not a valid identifier.

Why is my theme asking for thumbnails that do not exist?

WordPress generates intermediate sizes when the file is uploaded, not when the theme changes. If the theme requests a size registered later, regenerate them with wp media regenerate or the Regenerate Thumbnails plugin.

How do I add the sizes to a WooCommerce product gallery?

With the images field on the product in the WooCommerce REST API. Each entry can be an object with id (an existing attachment) or with src (a URL). The first image is the main one and the rest form the gallery.

What does it cost to process one image for a post?

1 use per destination, meaning per platform and format pair. Asking for Instagram post and LinkedIn post from the same master spends 2 uses.