Saltar al contenido principal
SocialCutter

CMS y webs

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.

  • 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 StrapiDestino SocialCutterMedida
Imagen destacada de la entradalinkedin post1200x627 (1.91:1)
Imagen dentro del contenidofacebook post1200x630 (1.91:1)
Tarjeta social (Open Graph)twitter post1200x675 (16:9)
Cabecera del sitiotwitter header1500x500 (3:1)
Ficha de producto en 1:1instagram post1080x1080 (1:1)
Segunda imagen verticalinstagram story1080x1920 (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.

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:

DetalleStrapi 4Strapi 5
Formato de respuesta RESTdata.attributescampos aplanados sobre data
Referencia de una entradaid numéricodocumentId
Subida de ficherosPOST /api/upload (FormData)POST /api/upload (FormData)
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

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.

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 "[email protected]" > 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:

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:

curl -s -X POST "$STRAPI_URL/api/upload" \
  -H "Authorization: Bearer $STRAPI_TOKEN" \
  -F "[email protected]" \
  -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.

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íntomaCausaSolución
403 en /api/uploadEl token no tiene la acción de subidaConcede el permiso del plugin upload o usa un token Full access
401 de StrapiToken ausente, mal copiado o revocadoManda Authorization: Bearer con un token activo
400 «files is required»Se envió JSON en vez de multipartUsa -F en curl o FormData en Node, con el campo files
La entrada guarda la URL, no la imagenSe envió una cadena en el campo de mediaEnvía el id del fichero, no la URL
413 en SocialCutterEl maestro pasa de 5 MBReduce el maestro antes de subirlo
429 en SocialCutterCuota del monedero agotadaConsulta 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.

Siguientes pasos

Preguntas frecuentes

¿Por qué no dejo que Strapi redimensione la imagen que subo?

Strapi genera puntos de ruptura (thumbnail, small, medium, large) que escalan el maestro manteniendo su proporción. No recorta a las proporciones exactas de cada red social, así que una foto 3:2 sigue siendo 3:2 en todos esos tamaños y no sirve como post 1:1 ni como story 9:16. SocialCutter sí devuelve cada medida exacta antes de subir.

¿Qué permisos necesita el token de API?

El token debe poder usar el plugin de subida (la acción de subida del plugin upload) y, si además actualizas la entrada en la misma operación, el permiso de edición del tipo de contenido. Con un token de tipo Full access funciona; con uno Custom hay que marcar esas acciones a mano.

¿Se vincula la imagen a la entrada al subirla o en una segunda llamada?

Las dos formas valen. POST /api/upload acepta los campos ref, refId y field para crear el fichero y enlazarlo en la misma petición. Si prefieres subir primero y enlazar después, guarda el id del fichero y edita la entrada con el campo de media.

¿Qué cambia entre Strapi 4 y Strapi 5?

Sobre todo el formato de las respuestas REST. Strapi 4 envuelve los campos en data.attributes y usa un id numérico; Strapi 5 aplana los campos sobre el objeto, usa documentId como referencia estable y en el endpoint de subida devuelve ese documentId además del id. El endpoint de subida sigue siendo POST /api/upload con FormData en ambos.

¿Cuánto cuesta procesar la imagen de una entrada?

1 uso por destino, es decir por cada combinación de plataforma y formato. Pedir Instagram post y LinkedIn post para el mismo maestro son 2 usos.