# Attach catalogue images to WooCommerce with SocialCutter
> Upload the master to the media library or pass a public URL, generate the catalogue sizes with SocialCutter and attach them to a product over REST.
- URL: https://socialcutter.theboomer.dev/en/guides/woocommerce/
- Idioma: en
- Familia: cms
- Actualizado: 2026-09-24
- Palabras clave: WooCommerce, WordPress, media library, REST API, Application Passwords, product gallery, PHP
## Why pre-generate the catalogue sizes

A product page shows up in the grid, in search, in the cart and in ads, and each slot has its own ratio.

The flow is: **one master goes in, SocialCutter returns every size, and WooCommerce gets the right one in each slot**. The `cover` crop, the default mode, is **centred**: it scales and splits the excess evenly on both sides. Frame the master with some room around the edges.

| Where it goes in the store | SocialCutter destination | Size |
|---|---|---|
| Main product image | `instagram` `post` | 1080x1080 (1:1) |
| Variant or second portrait image | `instagram` `story` | 1080x1920 (9:16) |
| Category banner | `facebook` `post` | 1200x630 (1.91:1) |
| Store header | `twitter` `header` | 1500x500 (3:1) |
| Ad or marketplace listing | `facebook` `story` | 1080x1920 (9:16) |

Sizes come from `GET /api/v1/platforms`, which is public.

## WooCommerce already generates its own sizes

When an attachment is uploaded, WordPress and WooCommerce generate their thumbnails: grid, product page, cart. The widths live in **Appearance → Customize → WooCommerce → Product Images** and the cropping in **Settings → Media**: rules from the CMS, not from you.

Pre-generating with SocialCutter pays off when:

- You need an **exact size** the CMS does not produce (1080x1920, 1500x500): the theme would crop to its own ratio.
- You do not want to depend on **regeneration**: a theme change leaves thumbnails out of date.
- You work in **batches**, with `POST /api/v1/images/batch`, preparing hundreds of products before touching the store.
- The same master feeds external channels (marketplace, ads) with other formats.

For a square thumbnail in the grid, the CMS sizes are plenty.

## Before you start

- WordPress 5.6 or newer over HTTPS, for Application Passwords.
- A SocialCutter `sc_` key from **Profile → API keys** in the dashboard.
- `ck_` and `cs_` keys from **WooCommerce → Settings → Advanced → REST API**.

```bash
export WP_URL="https://your-store.com"
export WP_USER="username"
export WP_APP_PASSWORD="abcd efgh ijkl mnop qrst uvwx"
export WC_CK="ck_..."
export WC_CS="cs_..."
export SC_KEY="sc_your_key"
```

## 1. The master: upload it to the media library or pass its URL

**Media library route.** The endpoint takes the file in the request body, with `Content-Disposition` for the name:

```bash
curl -s -X POST "$WP_URL/wp-json/wp/v2/media" \
  --user "$WP_USER:$WP_APP_PASSWORD" \
  -H "Content-Disposition: attachment; filename=master.jpg" \
  -H "Content-Type: image/jpeg" \
  --data-binary "@master.jpg" | jq -r '.id, .source_url'
```

They are created in **Users → Profile → Application Passwords** and travel over HTTP Basic: https://developer.wordpress.org/rest-api/reference/media/

**Public URL route.** If the master is already on a CDN, pass its URL as `source.value` and push only the outputs you will use.

## 2. Generate the sizes with SocialCutter

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

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

Every output is a public URL. `Idempotency-Key` makes retries safe. For a local file use `POST /api/v1/images/process/upload` (multipart, 5 MB maximum).

## 3. Attach the images to the product

The `images` field is a list: **the first image is the main one and the rest form the gallery**. Each entry accepts `id` (media library attachment) or `src` (a URL WooCommerce downloads), plus `alt` and `position`.

```bash
SQUARE=$(jq -r '.outputs[0].url' sc.json)
PORTRAIT=$(jq -r '.outputs[1].url' sc.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\": [
      { \"src\": \"$SQUARE\", \"alt\": \"T-shirt, front view\", \"position\": 0 },
      { \"src\": \"$PORTRAIT\", \"alt\": \"T-shirt, fabric detail\", \"position\": 1 }
    ]
  }" | jq '{id, images: [.images[] | {id, src, position}]}'
```

Swap `src` for `id` if the file is already in the media library so it is not duplicated. Docs: https://developer.woocommerce.com/docs/apis/rest-api/v3/products/ — fields change between releases: check the ones in your version.

## 4. PHP snippet with wp_remote_post

`wp_remote_post` is the standard WordPress function for calling external APIs and it accepts a `method` argument, which it forwards to `wp_remote_request`. That same call does the `PUT` to WooCommerce:

```php
<?php
/**
 * Plugin Name: SocialCutter Catalogue
 * Description: Generates catalogue sizes and attaches them to a WooCommerce product.
 */

function sc_catalogue_sizes( int $product_id, string $master_url ): array {
    $response = wp_remote_post( 'https://api.socialcutter.theboomer.dev/api/v1/images/process', array(
        'timeout' => 30,
        'headers' => array(
            'X-API-Key'       => SOCIALCUTTER_API_KEY,
            'Content-Type'    => 'application/json',
            'Idempotency-Key' => 'product-' . $product_id,
        ),
        'body' => wp_json_encode( array(
            'source'       => array( 'type' => 'url', 'value' => $master_url ),
            'destinations' => array(
                array( 'platform' => 'instagram', 'format' => 'post' ),
                array( 'platform' => 'instagram', 'format' => 'story' ),
            ),
        ) ),
    ) );

    if ( is_wp_error( $response ) ) {
        return array( 'error' => $response->get_error_message() );
    }

    $outputs = json_decode( wp_remote_retrieve_body( $response ), true )['outputs'] ?? array();
    if ( ! $outputs ) {
        return array( 'error' => 'The API returned no outputs' );
    }

    $images = array();
    foreach ( $outputs as $output ) {
        $images[] = array( 'src' => $output['url'], 'alt' => get_the_title( $product_id ) );
    }

    $request = wp_remote_post(
        rest_url( 'wc/v3/products/' . $product_id ),
        array(
            'method'  => 'PUT', // wp_remote_post forwards 'method' to wp_remote_request.
            'timeout' => 30,
            'headers' => array(
                'Authorization' => 'Basic ' . base64_encode( WC_CK . ':' . WC_CS ),
                'Content-Type'  => 'application/json',
            ),
            'body'    => wp_json_encode( array( 'images' => $images ) ),
        )
    );

    return json_decode( wp_remote_retrieve_body( $request ), true )['images'] ?? array();
}
```

Define `SOCIALCUTTER_API_KEY`, `WC_CK` and `WC_CS` in `wp-config.php`, never in the theme. To attach from the media library, `media_sideload_image( $url, $product_id, null, 'id' )` returns the attachment id.

## 5. No code: CSV importer and automators

The WooCommerce importer accepts an `Images` column with comma-separated URLs: it downloads them on import and builds the gallery in that order. Generate the sizes, build the CSV and use **Products → Import**. Reference: https://woocommerce.com/document/product-csv-import-suite-column-header-reference/

With n8n, Zapier or Make the pattern is one HTTP request step to `/api/v1/images/process` and another that updates the product: [n8n automation guide](/en/guides/n8n/).

## Cost

- **1 use per destination** (platform and format) per request; repeated ones are not charged twice.
- Failed processings are refunded.
- Plans include API and MCP: Free 3 uses/day, Basic 10, Pro 30, Agency 100.

## Common errors

| Symptom | Cause | Fix |
|---|---|---|
| `401` from the media library | Application Password mistyped or site without HTTPS | Regenerate the application password and check TLS |
| `401` from `/wc/v3/` | `ck_`/`cs_` keys revoked or pasted wrong | Recheck **WooCommerce → Settings → Advanced → REST API** |
| `400` saying "image is invalid" | `src` is not a public URL or not an image | Check the URL points at a SocialCutter output |
| The main image is not the one you wanted | Order of the `images` list | Reorder with `position`; the first entry wins |

## Next steps

- WordPress: [Integrate SocialCutter with WordPress and WooCommerce](/en/guides/wordpress/)
- PrestaShop: [PrestaShop product images over the Webservice API](/en/guides/prestashop/)
- Shopify: [Integrate SocialCutter with the Shopify Admin API](/en/guides/shopify/)
- Docs: https://docs.socialcutter.theboomer.dev