> ## Documentation Index
> Fetch the complete documentation index at: https://docs.1to1ai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Subir multimedia

> Sube un archivo y obtén un file_uuid para enviarlo en una acción.

Para enviar imágenes, video, audio o documentos, primero subes el archivo y
obtienes un `file_uuid`, que luego pasas a una acción de envío.

<Steps>
  <Step title="Pide una URL de subida">
    ```http theme={null}
    POST /files/request-upload
    ```

    Con `{ filename, mime_type }`. Responde `201` con `file_uuid`,
    `upload_url`, `upload_token`, `storage_path` y `expires_at`.
  </Step>

  <Step title="Sube el binario">
    ```http theme={null}
    PUT {upload_url}
    ```

    Sube el archivo a la `upload_url` que recibiste, antes de que expire
    (`expires_at`).
  </Step>

  <Step title="Confirma">
    ```http theme={null}
    POST /files/confirm
    ```

    Con `{ file_uuid }`. Responde `200` con `file_uuid`, `mime_type`,
    `size_bytes`, `requires_transcription`, `download_url` y `expires_at`. A
    partir de aquí el `file_uuid` es enviable.
  </Step>
</Steps>

<Note>
  Ya no declaras `size_bytes` al pedir la URL: el server mide el tamaño real al
  confirmar. En la respuesta de confirm, `requires_transcription` anticipa si
  este `file_uuid` requerirá una transcripción al usarlo en un envío — porque el
  tipo de archivo no es transcribible automáticamente o supera los 20 MB. Es una
  pista para prepararla con anticipación.

  Valida el tamaño del archivo localmente antes de subirlo: el límite por tipo y la cuota de almacenamiento se aplican al confirmar, con el binario ya transferido. Un negocio con el storage deshabilitado (`quota_bytes = 0`) o ya sobre el tope se rechaza en `request-upload` con `413` antes de transferir.
</Note>

## Límites de tamaño

| Categoría    | Máximo                                  |
| ------------ | --------------------------------------- |
| Imagen       | 5 MB                                    |
| Video        | 16 MB                                   |
| Audio        | 16 MB                                   |
| Documento    | 100 MB                                  |
| `text/plain` | 5 MB (tope propio, menor que Documento) |

<Note>
  **Uploads pendientes concurrentes:** un negocio puede tener a lo sumo **100 uploads sin confirmar** a la vez. Al superarlo, `request-upload` responde `429 TOO_MANY_PENDING_UPLOADS` con `Retry-After` — confirma o deja expirar los pendientes antes de pedir más. El flujo normal (request → PUT → confirm) nunca lo toca; solo afecta a integraciones que piden muchos `request-upload` por adelantado sin confirmar.
</Note>

## Enviar el archivo

Una vez confirmado, pasa el `file_uuid` a una acción de envío — como media
directa en `send_message`, o como header de una plantilla:

```jsonc theme={null}
{
  "conversation": { "phone": "+525512345678", "channel": "ch_7A9K2M4Q" },
  "actions": [
    {
      "type": "send_message",
      "template": {
        "name": "promo_verano",
        "language": "es_MX",
        "header": {
          "type": "document",
          "file_uuid": "7b8a1c2d-3e4f-5678-90ab-cdef12345678"
        },
        "body_variables": [{ "index": 1, "value": "Ana" }]
      }
    }
  ]
}
```

El `header.type` ∈ `image` | `video` | `document` lleva `file_uuid` (`document`
acepta un `filename` opcional) y una `transcription` **obligatoria** cuando la visión
no puede transcribir el archivo (el `requires_transcription` de arriba lo anticipa). En
`send_message` modo media, el `file_uuid` va a nivel de acción con `body` como caption
opcional (el audio no admite caption) y la misma regla de `transcription`.

<Tip>
  Si el preflight rechaza el grupo, la respuesta es un `4xx` síncrono y ninguna
  acción se ejecuta. Ver [Errores](/es/errors).
</Tip>

<Warning>
  **La media subida es efímera.** No es un almacén permanente: bajo presión de la cuota de
  almacenamiento, el sistema puede eliminar automáticamente los archivos **por antigüedad de
  último uso** (los que llevan más tiempo sin enviarse salen primero; reusar un `file_uuid`
  en cualquier conversación renueva su "último uso" y lo mantiene vivo para todas). Conserva
  tu propia copia si necesitas un registro durable. La API no ofrece una descarga estable por
  `file_uuid`; la única descarga es la `download_url` firmada que devuelve `/files/confirm`,
  válida por tiempo limitado. Reenviar un `file_uuid` ya eliminado responde `FILE_NOT_FOUND`.

  **Reutilización entre conversaciones.** Un mismo `file_uuid` puede enviarse a varias
  conversaciones sin volver a subirlo, pero el archivo es uno solo: si la limpieza por
  cuota lo elimina, se pierde en **todas** las conversaciones que lo referencian. Si
  necesitas independencia entre conversaciones, sube un archivo por conversación.
</Warning>

## Ejemplo ejecutable

El flujo completo en tres llamadas. El `file_uuid` que confirma el paso 3 es el
que después pasas a una acción de envío.

**1 · Pide la URL de subida**

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.1to1ai.com/api/v1/public/{slug}/files/request-upload" \
    -H "Authorization: Bearer sk_1to1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "filename": "comprobante.pdf",
      "mime_type": "application/pdf"
    }'
  ```

  ```json Respuesta 201 theme={null}
  {
    "file_uuid": "7b8a1c2d-3e4f-5678-90ab-cdef12345678",
    "upload_url": "https://<project>.supabase.co/storage/v1/object/upload/sign/business-files/...",
    "upload_token": "eyJ...",
    "storage_path": "<business_uuid>/api/pending/1729383000_7b8a1c2d....pdf",
    "expires_at": "2026-07-22T15:30:00Z"
  }
  ```
</CodeGroup>

**2 · Sube el binario** a la `upload_url` antes de que expire

```bash theme={null}
curl -X PUT "{upload_url}" \
  -H "Content-Type: application/pdf" \
  --data-binary @comprobante.pdf
```

**3 · Confirma**

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://app.1to1ai.com/api/v1/public/{slug}/files/confirm" \
    -H "Authorization: Bearer sk_1to1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "file_uuid": "7b8a1c2d-3e4f-5678-90ab-cdef12345678"
    }'
  ```

  ```json Respuesta 200 theme={null}
  {
    "file_uuid": "7b8a1c2d-3e4f-5678-90ab-cdef12345678",
    "mime_type": "application/pdf",
    "size_bytes": 48213,
    "requires_transcription": false,
    "download_url": "https://<project>.supabase.co/storage/v1/object/sign/...",
    "expires_at": "2026-07-22T16:30:00Z"
  }
  ```
</CodeGroup>

## Con transcripción obligatoria

Si el archivo no es auto-transcribible por la visión (tipo no soportado como
`.docx`/`.xlsx`, o mayor a 20 MB), la respuesta de `/confirm` lo
anticipa con `requires_transcription: true`:

```json Respuesta 200 (requiere transcripción) theme={null}
{
  "file_uuid": "9c1b2a3d-4e5f-6789-01ab-cdef23456789",
  "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "size_bytes": 91240,
  "requires_transcription": true,
  "download_url": "https://<project>.supabase.co/storage/v1/object/sign/...",
  "expires_at": "2026-07-22T16:30:00Z"
}
```

En ese caso, al enviarlo debes incluir `transcription` — como media primaria en
`send_message` o como `template.header.transcription` — o la acción falla con
`TRANSCRIPTION_REQUIRED` / `TEMPLATE_HEADER_TRANSCRIPTION_MISSING`:

```bash Enviar con transcripción theme={null}
curl -X POST "https://app.1to1ai.com/api/v1/public/{slug}/conversation/actions" \
  -H "Authorization: Bearer sk_1to1_tu_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation": { "phone": "+525512345678", "channel": "ch_7A9K2M4Q" },
    "actions": [
      {
        "type": "send_message",
        "file_uuid": "9c1b2a3d-4e5f-6789-01ab-cdef23456789",
        "body": "Tu contrato 📄",
        "transcription": "Contrato de servicio (3 páginas): plan Pro mensual, renovación automática, cancelable con 30 días de aviso."
      }
    ]
  }'
```
