# Adjunta en Airtable las medidas generadas con SocialCutter
> Genera los formatos con SocialCutter y adjunta la URL de salida a un campo attachment por la API de Airtable: flujo directo, Python, curl y errores.
- URL: https://socialcutter.theboomer.dev/guias/airtable/
- Idioma: es
- Familia: ia
- Actualizado: 2026-09-24
- Palabras clave: Airtable, attachment, API, token personal, Python, curl, SocialCutter
## Por qué el flujo es directo en Airtable

Un campo de tipo attachment (`multipleAttachments`) acepta, al escribir, una lista de objetos con `url`. Airtable descarga el fichero desde esa URL y se queda con su propia copia. Como SocialCutter devuelve una URL pública por cada formato generado, no hace falta subir binarios ni montar ningún intermediario: generas, adjuntas y ya está.

La frontera sigue siendo la misma que en el resto de integraciones: SocialCutter **genera imágenes, no publica**. El recorte es **centrado**, sin detección de sujeto ni análisis de contenido. Publicar o adjuntar es del llamante.

## Por qué pre-generar antes de adjuntar

Airtable muestra la miniatura del adjunto, pero no recorta la imagen a la proporción que pide cada hueco. Si guardas un solo maestro y lo reutilizas para la miniatura de la galería, el brief de un reel y la portada del registro, cada vista lo escala a su manera.

| Hueco en el registro | Destino SocialCutter | Medida |
|---|---|---|
| Miniatura cuadrada de la galería | `instagram` `post` | 1080x1080 (1:1) |
| Imagen vertical para un brief de reel | `instagram` `story` | 1080x1920 (9:16) |
| Tarjeta apaisada o vista de galería | `twitter` `post` | 1200x675 (16:9) |
| Portada del registro o de la vista | `facebook` `post` | 1200x630 (1.91:1) |
| Banner ancho para una cabecera | `linkedin` `cover` | 1128x191 (5.9:1) |

Pre-generar las cinco son 5 usos y sale en una única petición, así que el registro queda completo con las medidas ya resueltas.

## Cómo escribe la API en un campo attachment

- **Forma de escritura:** un array de objetos. Basta `url`, y `filename` es opcional pero recomendable para controlar el nombre del adjunto.
- **Airtable descarga el fichero.** La URL tiene que ser alcanzable desde fuera, sin login ni firma caducada, y devolver un `Content-Type` de imagen.
- **Lo que envías es lo que queda.** Los adjuntos que no incluyas en el array se eliminan del campo. Para conservarlos, mándalos otra vez con su `id`: el objeto que devuelve la lectura sirve tal cual.
- **Al leer,** las URLs son de `v5.airtableusercontent.com` y caducan a las dos horas. Son para descargar, no para incrustar en otra web.
- **Límites del plan:** hasta 5 GB por fichero, con un almacenamiento por base que va de 1 GB en Free a 1 TB en Enterprise. La referencia completa está en https://airtable.com/developers/web/api/field-model.

## Crea el token personal

Entra en https://airtable.com/create/tokens, añade los permisos `data.records:write` (y `schema.bases:read` si quieres listar los campos de la tabla) y da acceso a la base concreta. Se envía en la cabecera `Authorization: Bearer pat...`. Guarda el token y el identificador de la base en variables de entorno, nunca en el código.

## Flujo directo con Python

```python
import os
import requests

SC = "https://api.socialcutter.theboomer.dev"
BASE = os.environ["AIRTABLE_BASE_ID"]
TABLE = os.environ["AIRTABLE_TABLE_ID"]

sc = requests.Session()
sc.headers["X-API-Key"] = os.environ["SOCIALCUTTER_API_KEY"]

at = requests.Session()
at.headers["Authorization"] = f"Bearer {os.environ['AIRTABLE_TOKEN']}"

# 1. Generar todos los formatos de una vez
resp = sc.post(f"{SC}/api/v1/images/process", json={
    "source": {"type": "url", "value": "https://example.com/maestro.jpg"},
    "destinations": [
        {"platform": "instagram", "format": "post"},
        {"platform": "instagram", "format": "story"},
        {"platform": "twitter", "format": "post"},
    ],
}, timeout=60)
resp.raise_for_status()
salidas = resp.json()["outputs"]

# 2. Adjuntar las salidas al registro: Airtable descarga y rehospeda
adjuntos = [
    {"url": out["url"], "filename": f"{out['platform']}-{out['format']}.webp"}
    for out in salidas
]

r = at.patch(f"https://api.airtable.com/v0/{BASE}/{TABLE}/{os.environ['RECORD_ID']}",
             json={"fields": {"Adjuntos": adjuntos}}, timeout=60)
r.raise_for_status()
for att in r.json()["fields"]["Adjuntos"]:
    print(att["filename"], att["size"], att["type"])

# 3. Alta de un registro nuevo con la imagen cuadrada ya adjunta
nuevo = at.post(f"https://api.airtable.com/v0/{BASE}/{TABLE}", json={
    "records": [{"fields": {
        "Nombre": "Campana de septiembre",
        "Adjuntos": [{"url": salidas[0]["url"], "filename": "instagram-post.webp"}],
    }}],
}, timeout=60)
nuevo.raise_for_status()
```

Los endpoints de lote admiten 10 registros por petición, y conviene espaciarlos para no pasar de 5 peticiones por segundo y base.

## Flujo directo con curl

```bash
curl -s -X PATCH "https://api.airtable.com/v0/$BASE_ID/$TABLE_ID/$RECORD_ID" \
  -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"fields\":{\"Adjuntos\":[{\"url\":\"$OUTPUT_URL\",\"filename\":\"instagram-post.webp\"}]}}" \
  | python3 -m json.tool
```

Sustituye `$BASE_ID` por `app...`, `$TABLE_ID` por `tbl...` y `$RECORD_ID` por `rec...`. Para el nombre del campo puedes usar el identificador `fld...` en lugar del nombre, que es lo más estable si alguien renombra la columna.

## Cuando Airtable no puede descargar la URL

Si el campo no se rellena y la respuesta habla de un fallo de subida, casi siempre es que Airtable no ha podido descargar el fichero. Comprueba que la URL es pública, que no depende de una sesión y que devuelve la imagen con su `Content-Type`. El aviso típico de la interfaz es «Couldn't upload. Try adding again» y detrás hay un 403 del dominio de subida de Airtable; el soporte lo documenta en https://support.airtable.com/docs/attachment.

Cuando la URL no se puede exponer, queda la subida directa:

```bash
curl -s -X POST \
  "https://content.airtable.com/v0/$BASE_ID/$RECORD_ID/$FIELD_ID/uploadAttachment" \
  -H "Authorization: Bearer $AIRTABLE_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"contentType":"image/webp","file":"<base64>","filename":"instagram-post.webp"}'
```

Ese endpoint acepta hasta 5 MB por fichero, exactamente el mismo techo que el maestro que admite SocialCutter, así que el respaldo cubre el mismo rango que el flujo por URL.

## Coste

- **1 uso por destino** (plataforma y formato) por petición a SocialCutter. Los destinos repetidos no se cobran dos veces y los procesamientos fallidos se devuelven.
- Planes: 0 EUR (3 usos/día), 3 EUR (10/día), 9 EUR (30/día) y 29 EUR (100/día), todos con API y MCP.
- La API de Airtable no se cobra por llamada, pero sí cuenta en el límite mensual de tu plan (1.000 llamadas al mes en Free).

## Errores típicos

| Código | Origen | Significado |
|---|---|---|
| 400 | SocialCutter | Payload inválido: plataforma o formato desconocido |
| 401 | SocialCutter | Falta la cabecera `X-API-Key` o la clave no vale |
| 413 | SocialCutter | El maestro supera 5 MB |
| 429 | SocialCutter | Cuota agotada: consulta `GET /api/v1/wallet` |
| 401 | Airtable | Token ausente, mal formado o sin acceso a esa base |
| 403 | Airtable | Airtable no ha podido descargar la URL del adjunto |
| 404 | Airtable | La base, la tabla o el registro no existen |
| 422 | Airtable | Campo desconocido o valor que no encaja con el tipo attachment |
| 429 | Airtable | Más de 5 peticiones por segundo y base: espera unos 30 segundos |

## Siguientes pasos

- Camino con código: [Procesa imágenes con la API de SocialCutter desde Python](/guias/python/) y [desde la terminal con curl](/guias/curl/)
- Sin código: [Automatiza el recorte de imágenes con n8n](/guias/n8n/), [con Zapier](/guias/zapier/) y [con Make](/guias/make/)
- Panorama: [Automatizar imágenes para redes sociales: los 4 caminos](/guias/automatizar-imagenes-redes-sociales/)
- Referencia de la API de SocialCutter: https://docs.socialcutter.theboomer.dev
- Campo de tipo attachment en Airtable: https://airtable.com/developers/web/api/field-model