# Sube imágenes a HubSpot con la Files API
> Sube las imágenes que genera SocialCutter al File Manager de HubSpot por la Files API, con multipart o desde una URL, y úsalas en correos, landings y redes.
- URL: https://socialcutter.theboomer.dev/guias/hubspot/
- Idioma: es
- Familia: cms
- Actualizado: 2026-09-24
- Palabras clave: HubSpot, Files API, File Manager, import-from-url, scopes, SocialCutter, email marketing
## Por qué pasar por el File Manager

El **File Manager** de HubSpot es la biblioteca de medios del portal: todo lo que insertas en correos, páginas, landings y posts de blog se sirve desde ahí, a través del CDN de HubSpot. SocialCutter genera versiones recortadas de forma centrada a las medidas exactas de cada destino y devuelve una URL pública por salida; si esas imágenes van a vivir dentro de HubSpot, conviene subirlas al File Manager en lugar de enlazar URLs externas: así las gestionas y las reutilizas.

La frontera importa: **SocialCutter genera imágenes, no publica.** HubSpot solo almacena y sirve el fichero.

## Requisitos: Private app y scopes

Crea una **Private app** en Configuración → Integraciones → Private apps y marca los scopes del Files API:

| Scope | Para qué sirve |
|---|---|
| `files` | Leer, subir y archivar ficheros y carpetas |
| `files.ui_hidden.read` | Acceder a ficheros ocultos que no aparecen en el listado público |

El token empieza por `pat-` y viaja en la cabecera `Authorization: Bearer pat-…`. La referencia completa de la Files API, con los scopes y campos vigentes, está en https://developers.hubspot.com/docs/api-reference/latest/files/guide.

## Subir un fichero por multipart

`POST /files/v3/files` acepta `multipart/form-data`. Un fichero por petición:

```bash
curl -s -X POST "https://api.hubapi.com/files/v3/files" \
  -H "Authorization: Bearer pat-tu_token" \
  -F "file=@instagram-post.jpg" \
  -F "folderPath=/socialcutter" \
  -F 'options={"access":"PUBLIC_INDEXABLE"}'
```

| Campo | Obligatorio | Descripción |
|---|---|---|
| `file` | Sí | El binario a subir |
| `folderId` o `folderPath` | Uno de los dos | Carpeta destino; se recomienda no subir a la raíz |
| `fileName` | No | Nombre final; si se omite se genera del contenido |
| `options` | No | JSON con `access` y, opcionalmente, `ttl` (de 1 día a 1 año) |

La respuesta `201` trae `id`, `path`, `url`, `defaultHostingUrl`, `access`, `width`, `height` e `isUsableInContent`. Guarda el `id` y la `url`: son lo que usas después en el editor de correos o en el selector de imágenes.

## Subir desde una URL con import-from-url

Cuando SocialCutter ya te devuelve URLs, no hace falta descargar y resubir a mano. HubSpot importa desde una URL de forma asíncrona:

```bash
curl -s -X POST "https://api.hubapi.com/files/v3/files/import-from-url/async" \
  -H "Authorization: Bearer pat-tu_token" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://cdn.socialcutter.theboomer.dev/out/twitter-post.jpg",
    "access": "PUBLIC_INDEXABLE",
    "folderPath": "/socialcutter",
    "duplicateValidationStrategy": "REJECT",
    "duplicateValidationScope": "EXACT_FOLDER"
  }'
```

La respuesta `202` devuelve un `id` de tarea. Consultas el estado con `GET /files/v3/files/import-from-url/async/tasks/{taskId}/status`, que responde `PENDING`, `PROCESSING`, `COMPLETE` o `CANCELED`. Con `COMPLETE` el fichero ya está en el File Manager.

Aviso de versiones: HubSpot ha ido moviendo el Files API a rutas versionadas (de `files/v3` a esquemas como `files/2026-09`), y los nombres de scope han cambiado entre versiones. Antes de fijar rutas en tu código, confirma la ruta y los scopes vigentes en la documentación oficial enlazada arriba.

## Niveles de acceso y dónde se puede usar cada fichero

| `access` | ¿Se puede insertar en correos, páginas y landings? |
|---|---|
| `PUBLIC_INDEXABLE` | Sí, y además se puede indexar en buscadores |
| `PUBLIC_NOT_INDEXABLE` | Sí, pero sin indexación |
| `PRIVATE` | No directamente: requiere URL firmada e `isUsableInContent` es `false` |
| `SENSITIVE` | No: pensado para datos, no para contenido |

Si la imagen va a un correo o una landing, usa acceso público. Un fichero privado no se renderiza en el correo porque el cliente de email no puede firmar la URL.

## Tabla de tamaños recomendados por HubSpot

Estas son medidas orientativas que HubSpot publica para los sitios donde se usan imágenes dentro del portal. Para las medidas exactas que **genera** SocialCutter por red social, la referencia es la guía interna de medidas (enlazada abajo).

| Ubicación en HubSpot | Tamaño recomendado | Proporción |
|---|---|---|
| Imagen dentro de un correo | 600 px de ancho | Variable (el ancho de plantilla manda) |
| Cabecera de correo | 600x200 | 3:1 |
| Imagen destacada de blog | 1200x628 | ~1.91:1 |
| Imagen para compartir en redes (social share) | 1200x630 | 1.91:1 |
| Miniatura de blog | 400x400 | 1:1 |
| Hero / ancho completo de landing | 1920x1080 (≥1200 px de ancho) | 16:9 |
| Banner del sitio | 2500x625 | 4:1 |
| Imagen de autor | 500x500 | 1:1 |

Fuentes de HubSpot: su guía de medidas para redes (https://blog.hubspot.com/marketing/ultimate-guide-social-media-image-dimensions-infographic) y la de imágenes para web (https://blog.hubspot.com/website/image-size-for-website). Ojo con las diferencias finas: el social share de HubSpot es 1200x630 y su destacada de blog 1200x628; el destino más cercano del catálogo de SocialCutter es `facebook` + `post` (1200x630). Si necesitas una medida que no produce el catálogo, recórtala aparte.

## Flujo: de SocialCutter al File Manager

1. Procesa la maestra: `POST /api/v1/images/process` con `source` y `destinations`.
2. Recorre el array `outputs` y, por cada URL, lanza un `import-from-url/async`.
3. Espera a `COMPLETE` por tarea y recoge la `url` final.
4. Inserta esa URL en el correo, la landing o el post de blog.

```python
import time, requests

HUB = {"Authorization": "Bearer pat-tu_token"}
BASE = "https://api.hubapi.com/files/v3/files/import-from-url/async"

def subir(url, folder="/socialcutter"):
    r = requests.post(BASE, headers={**HUB, "Content-Type": "application/json"},
                      json={"url": url, "access": "PUBLIC_INDEXABLE", "folderPath": folder},
                      timeout=30)
    r.raise_for_status()
    task = r.json()["id"]
    while True:
        s = requests.get(f"{BASE}/tasks/{task}/status", headers=HUB, timeout=30).json()
        if s.get("status") in ("COMPLETE", "CANCELED"):
            return s
        time.sleep(2)

for out in outputs:            # outputs viene de SocialCutter
    print(out["platform"], subir(out["url"]))
```

## Coste

| Concepto | Valor |
|---|---|
| Coste por procesamiento | 1 uso por destino (plataforma y formato) |
| Destinos repetidos en la misma petición | No se cobran dos veces |
| Subida al File Manager de HubSpot | Sin coste adicional, dentro de los límites del portal |
| Planes de SocialCutter | 0, 3, 9 y 29 EUR, con API y MCP incluidos |

Una petición de Instagram post + LinkedIn post + X post consume 3 usos de SocialCutter y produce 3 subidas a HubSpot.

## Errores típicos

| Error | Qué pasa realmente | Qué hacer |
|---|---|---|
| `401`/`403` al subir | El token no tiene el scope `files` | Añade el scope en la Private app y regenera el token |
| La imagen no se ve en el correo | Se subió como `PRIVATE` | Sube con acceso público (`PUBLIC_INDEXABLE` o `PUBLIC_NOT_INDEXABLE`) |
| `400` en el upload por multipart | Falta `folderId`/`folderPath` o la carpeta no existe | Crea la carpeta antes, o usa `import-from-url`, que puede crearla |
| `429` | Límite de peticiones del portal | Añade reintentos con espera exponencial |
| Nombre duplicado | Ya existe un fichero igual en la carpeta | Usa `duplicateValidationStrategy: RETURN_EXISTING` o `overwrite` |
| La URL de destino no importa | HubSpot no pudo descargar la imagen | Comprueba que la URL es pública y accesible sin sesión |

## Siguientes pasos

- Guía de la API con curl: [Procesa imágenes con la API desde la terminal](/guias/curl/)
- Python: [Procesa imágenes con la API de SocialCutter desde Python](/guias/python/)
- No-code: [Automatiza el recorte de imágenes con Zapier](/guias/zapier/), [Make](/guias/make/) o [n8n](/guias/n8n/)
- Estrategia: [Automatizar imágenes para redes sociales: 4 caminos](/guias/automatizar-imagenes-redes-sociales/)
- Medidas: [Medidas de redes sociales: tamaños y proporciones](/guias/medidas-redes-sociales/)
- Documentación de la API: https://docs.socialcutter.theboomer.dev