# Sube imágenes a la mediateca de Strapi con SocialCutter
> Sube la imagen a la mediateca de Strapi por POST /api/upload con una petición multipart y úsala en tus entradas a su medida.
- URL: https://socialcutter.theboomer.dev/guias/strapi/
- Idioma: es
- Familia: cms
- Actualizado: 2026-09-24
- Palabras clave: Strapi, mediateca, Media Library, api/upload, multipart, token de API, relación media, Node
## Por qué generar los tamaños antes de subir

Una entrada de blog o una ficha de producto enseña la misma imagen en cuatro sitios: la tarjeta del listado, la cabecera del artículo, la tarjeta de Open Graph y la miniatura de una red social. Cada hueco pide una proporción distinta. Si subes el maestro y dejas que cada hueco lo recorte con CSS, el resultado depende del navegador y de la pantalla.

El flujo es: **un maestro entra, SocialCutter devuelve cada medida y la mediateca de Strapi recibe el fichero ya recortado**. El recorte del modo `cover` (el que se aplica por defecto) es **centrado**: escala la imagen y reparte el sobrante por igual a los dos lados. No hay detección de sujeto ni ningún paso que decida qué parte sobra.

| Hueco en Strapi | Destino SocialCutter | Medida |
|---|---|---|
| Imagen destacada de la entrada | `linkedin` `post` | 1200x627 (1.91:1) |
| Imagen dentro del contenido | `facebook` `post` | 1200x630 (1.91:1) |
| Tarjeta social (Open Graph) | `twitter` `post` | 1200x675 (16:9) |
| Cabecera del sitio | `twitter` `header` | 1500x500 (3:1) |
| Ficha de producto en 1:1 | `instagram` `post` | 1080x1080 (1:1) |
| Segunda imagen vertical | `instagram` `story` | 1080x1920 (9:16) |

El catálogo completo de plataformas y formatos sale de `GET /api/v1/platforms`, que es público, y está resumido en la [guía de medidas por red social](/guias/medidas-redes-sociales/).

## Qué hace Strapi por su cuenta (y qué no)

El plugin de subida de Strapi genera puntos de ruptura: `thumbnail`, `small`, `medium` y `large`. Son **reescalados que conservan la proporción del original**, no recortes a una proporción concreta. Una foto 3:2 sigue siendo 3:2 en los cuatro. Por eso no sustituyen a un recorte por plataforma: sirven para no descargar 4000 px en el móvil, no para llenar un hueco 1:1 o 9:16.

Tampoco hay un 4:5 en el catálogo de SocialCutter: lo vertical es 9:16 (1080x1920) y lo cuadrado es 1:1 (1080x1080). Si necesitas 4:5 exacto, recorta fuera.

## Antes de empezar: token, permisos y versión

**Token de API.** En el panel de Strapi, Settings → API Tokens permite crear un token con tipo *Full access*, *Read-only* o *Custom*. El valor se muestra una sola vez y viaja en la cabecera `Authorization: Bearer`.

**Permisos.** Un token de tipo *Custom* lleva la misma matriz de permisos que un rol: hay que concederle la acción de subida del plugin `upload` y, si en la misma llamada editas la entrada, la acción de edición del tipo de contenido (`update`). Con *Full access* no hace falta marcar nada. Referencia: https://docs.strapi.io/cms/features/api-tokens

**Versión.** Strapi publica versiones mayores con cambios de formato en la API REST, así que fija la que usas y revisa la documentación antes de subir de una 4 a una 5:

| Detalle | Strapi 4 | Strapi 5 |
|---|---|---|
| Formato de respuesta REST | `data.attributes` | campos aplanados sobre `data` |
| Referencia de una entrada | `id` numérico | `documentId` |
| Subida de ficheros | `POST /api/upload` (FormData) | `POST /api/upload` (FormData) |

```bash
export STRAPI_URL="https://tu-strapi.com"
export STRAPI_TOKEN="tu_token_de_api"
export SC_KEY="sc_tu_clave"
```

## 1. Procesa el maestro con SocialCutter

```bash
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://tu-cdn.com/maestro.jpg" },
    "destinations": [
      { "platform": "linkedin", "format": "post" },
      { "platform": "instagram", "format": "post" }
    ]
  }' > sc.json

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

La respuesta trae `image_id` y una URL por destino. Si el maestro solo existe en tu disco, usa `POST /api/v1/images/process/upload` (multipart, máximo 5 MB).

## 2. Sube la imagen a la mediateca

`POST /api/upload` es multipart y el único campo obligatorio es `files`. Acepta varias entradas en la misma petición y devuelve un objeto por fichero con `id`, `url` y el bloque `formats`.

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

curl -s -X POST "$STRAPI_URL/api/upload" \
  -H "Authorization: Bearer $STRAPI_TOKEN" \
  -F "files=@linkedin.jpg" > media.json

jq '.[0] | {id, url, mime, width, height}' media.json
```

En **Node (18 o superior)** hay `FormData` y `Blob` globales, así que no hace falta ninguna dependencia para el multipart. El truco es descargar la salida como `Blob` y colgarla del formulario con un nombre de fichero:

```js
const SC_KEY = process.env.SC_KEY
const STRAPI_URL = process.env.STRAPI_URL
const STRAPI_TOKEN = process.env.STRAPI_TOKEN

async function processMaster(masterUrl) {
  const res = await fetch('https://api.socialcutter.theboomer.dev/api/v1/images/process', {
    method: 'POST',
    headers: { 'X-API-Key': SC_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      source: { type: 'url', value: masterUrl },
      destinations: [{ platform: 'linkedin', format: 'post' }]
    })
  })
  if (!res.ok) throw new Error(`SocialCutter ${res.status}`)
  return res.json()
}

async function uploadToMediaLibrary(imageUrl, filename) {
  const bin = await fetch(imageUrl)
  const blob = await bin.blob()

  const form = new FormData()
  form.append('files', blob, filename)

  const res = await fetch(`${STRAPI_URL}/api/upload`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${STRAPI_TOKEN}` },
    body: form
  })
  if (!res.ok) throw new Error(`Strapi upload ${res.status}`)
  const [file] = await res.json()
  return file // { id, documentId?, url, formats }
}
```

Documentación del endpoint: https://docs.strapi.io/cms/api/rest/upload

## 3. Enlaza la imagen con la entrada

Hay dos caminos y ninguno necesita un plugin.

**En la misma subida.** `/api/upload` acepta `ref` (el UID del tipo de contenido), `refId` (la referencia de la entrada, `documentId` en Strapi 5) y `field` (el nombre del campo de media). El fichero nace ya enlazado:

```bash
curl -s -X POST "$STRAPI_URL/api/upload" \
  -H "Authorization: Bearer $STRAPI_TOKEN" \
  -F "files=@linkedin.jpg" \
  -F "ref=api::article.article" \
  -F "refId=abc123xyz" \
  -F "field=cover"
```

**En una segunda llamada.** Sube primero, guarda el `id` del fichero y edita la entrada. Para un campo de media simple se manda el id; para uno múltiple, un array de ids. La forma del cuerpo depende de la versión: el envoltorio `data` es el mismo, pero el formato de la respuesta no.

```bash
FILE_ID=$(jq -r '.[0].id' media.json)

curl -s -X PUT "$STRAPI_URL/api/articles/abc123xyz?populate=cover" \
  -H "Authorization: Bearer $STRAPI_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"data\": {\"cover\": $FILE_ID}}"
```

Con `?populate=cover` la respuesta trae el objeto de media completo y puedes comprobar que el `id` coincide.

## Coste

- **1 uso por destino** (plataforma y formato) por petición; los repetidos no se cobran dos veces.
- Los procesamientos fallidos se devuelven.
- Planes 0/3/9/29 EUR, todos con API y MCP.

## Errores típicos

| Síntoma | Causa | Solución |
|---|---|---|
| `403` en `/api/upload` | El token no tiene la acción de subida | Concede el permiso del plugin `upload` o usa un token Full access |
| `401` de Strapi | Token ausente, mal copiado o revocado | Manda `Authorization: Bearer` con un token activo |
| `400` «files is required» | Se envió JSON en vez de multipart | Usa `-F` en curl o `FormData` en Node, con el campo `files` |
| La entrada guarda la URL, no la imagen | Se envió una cadena en el campo de media | Envía el **id** del fichero, no la URL |
| `413` en SocialCutter | El maestro pasa de 5 MB | Reduce el maestro antes de subirlo |
| `429` en SocialCutter | Cuota del monedero agotada | Consulta `GET /api/v1/credits` o sube de plan |

## Sin código

Un automatizador encadena los mismos pasos con nodos: disparador, nodo HTTP a `/api/v1/images/process` y un nodo HTTP a `/api/upload` con el fichero en multipart. El patrón general está en la [guía de automatización de imágenes para redes sociales](/guias/automatizar-imagenes-redes-sociales/).

## Siguientes pasos

- WordPress y WooCommerce: [Integra SocialCutter con WordPress y WooCommerce](/guias/wordpress/)
- Shopify: [Integra SocialCutter con la Admin API de Shopify](/guias/shopify/)
- Terminal: [Procesa imágenes con la API desde la terminal (curl)](/guias/curl/)
- Automatización: [Automatiza el recorte de imágenes con n8n](/guias/n8n/)
- Documentación: https://docs.socialcutter.theboomer.dev