# Límites y validaciones

Qué comprueba el SDK antes de enviar cada petición, qué comprueba solo el servidor y los límites de paginación, archivos y configuración.

El SDK rechaza algunos datos antes de enviarlos para que no gastes una petición en algo que el servidor va a devolver con un 400. Esta página reúne esas comprobaciones, las que solo hace el servidor y los límites de paginación y de archivos.

## Las constantes `SDK_LIMITS`

Los dos SDK exportan `SDK_LIMITS` con los mismos valores. En JavaScript es un objeto; en Python, un diccionario con las mismas claves (y cada valor también como constante suelta).

| Constante | Valor | Qué limita |
| - | - | - |
| `TICKET_TITLE_MIN` | 5 | Longitud mínima del título de un ticket. |
| `TICKET_TITLE_MAX` | 500 | Longitud máxima del título de un ticket. |
| `TICKET_DESCRIPTION_MIN` | 10 | Longitud mínima de la descripción de un ticket. |
| `MESSAGE_CONTENT_MIN` | 1 | Longitud mínima de un mensaje. |
| `MESSAGE_CONTENT_MAX` | 5000 | Longitud máxima de un mensaje. |
| `FILE_NAME_MAX` | 255 | Longitud máxima del nombre de un archivo. |
| `EXTERNAL_ID_MAX` | 255 | Longitud máxima del identificador externo de un usuario. |
| `AVATAR_URL_MAX` | 2048 | Longitud máxima de la dirección del avatar. |

## Qué se comprueba y dónde

| Campo | Regla | Dónde se comprueba |
| - | - | - |
| Ticket: `title` | Obligatorio, de 5 a 500 caracteres | SDK y servidor |
| Ticket: `description` | Obligatoria, 10 caracteres como mínimo | SDK y servidor |
| Ticket: `user.externalId` | Obligatorio, 255 caracteres como máximo | Solo servidor |
| Ticket: `category` y `priority` | Un valor de su enumerado | Servidor (en Python, también el modelo) |
| Mensaje: `content` | De 1 a 5000 caracteres | SDK y servidor |
| Usuario: `externalId` | Obligatorio, 255 caracteres como máximo | SDK y servidor |
| Usuario: `email` | Formato de correo si lo envías | SDK |
| Usuario: `avatarUrl` | Dirección válida, 2048 caracteres como máximo | SDK |
| Archivo: nombre personalizado | 255 caracteres como máximo | SDK |
| Error notificado: `message` | Obligatorio; 2000 caracteres como máximo | SDK (que no esté vacío) y servidor (longitud) |
| Error notificado: `module` | Obligatorio; 100 caracteres como máximo | SDK (que no esté vacío) y servidor (longitud) |
| Error notificado: `context` | 64 KB como máximo, serializado en JSON | Servidor |
| Error notificado: `fingerprint` y `tags` | De 1 a 8 textos de hasta 200 caracteres; hasta 20 etiquetas de texto, número o booleano | Servidor |
| `errors.reportBatch`: la lista | De 1 a 25 errores | SDK de JavaScript y servidor |
| `errors.silence`: `hours` | Entero de 1 a 672 | SDK de JavaScript y servidor |

Diferencias entre los dos lenguajes que conviene conocer:

- **Cómo avisa.** En JavaScript la comprobación la hace el método del servicio y lanza un `UtiliaSDKError` con código `VALIDATION_ERROR`. En Python la hacen los modelos de entrada al construirse y lanzan un `pydantic.ValidationError`; solo el nombre del archivo de `files.upload()` y la verificación de avisos lanzan un `UtiliaSDKError`. Cómo capturar cada caso está en [Gestionar errores](https://os.utilia.ai/docs/developers/sdk/error-handling#errores-antes-de-enviar-la-petición).
- **Direcciones.** Python solo acepta direcciones `http` o `https` con dominio; JavaScript acepta cualquier dirección que el constructor `URL` sepa leer.
- **Identificador vacío.** JavaScript rechaza un `externalId` vacío o solo con espacios; Python lo deja pasar y es el servidor quien lo rechaza.

Las funciones de comprobación son internas: no se pueden importar desde tu código.

**La captura de errores no rechaza: ajusta.** `errors.capture` y `captureMessage` (`capture_exception` y `capture_message`) recortan los textos a su límite, quitan las etiquetas que no son texto, número o booleano y, si el contexto pasa de unos 60 KB, lo sustituyen por `contextTruncated: true` y un extracto en `contextPreview`. Así un campo fuera de límites no hace rechazar el lote entero. `report` y `reportBatch`, en cambio, envían lo que les das y el servidor responde `400` si algo no cumple.

### JavaScript

```typescript
import { ErrorCode, UtiliaSDKError } from '@utilia-os/sdk-js';

try {
  await sdk.tickets.create({
    user: { externalId: 'usr_123' },
    title: 'Bug', // 3 caracteres: el mínimo es 5
    description: 'Detalle del problema al cargar la página de ajustes.',
  });
} catch (error) {
  if (error instanceof UtiliaSDKError && error.code === ErrorCode.VALIDATION_ERROR) {
    console.log(error.message); // "title must be at least 5 characters long (got 3)"
    console.log(error.statusCode); // undefined: la petición no salió
  }
}
```

### Python

```python
from pydantic import ValidationError

from utilia_sdk import CreateTicketInput, CreateTicketUser

try:
    data = CreateTicketInput(
        user=CreateTicketUser(external_id="usr_123"),
        title="Bug",  # 3 caracteres: el mínimo es 5
        description="Detalle del problema al cargar la página de ajustes.",
    )
except ValidationError as error:
    print(error.errors())  # "title must be at least 5 chars (got 3)"
```

## Límites de uso

**Límites que conviene recordar**

- Tickets por página: 100 como máximo. Por defecto, 20.
- Errores notificados por página: 200 como máximo. Por defecto, 50.
- Tiempo de espera por petición: 30 s. 30000 en JavaScript y 30.0 en Python.
- Intentos por petición: 3. Solo en lo que se puede repetir sin duplicar nada.

**Límite de peticiones.** Si lo superas, el servidor responde 429 y el SDK lanza un error `RATE_LIMITED` después de agotar sus reintentos. Desde la 4.6.0 espera lo que pide `Retry-After` y, si la espera pasa de un minuto (`maxRetryAfterMs`, `max_retry_after`), no reintenta: el error trae `retryAfterMs` (`retry_after`). Las cifras y las cabeceras están en [Límites de uso y CORS](https://os.utilia.ai/docs/developers/getting-started/rate-limits-and-cors). El envío de errores tiene sus propios límites, que no gastan el cupo por hora de la clave: los explica [Capturar los errores de tu aplicación](https://os.utilia.ai/docs/developers/sdk/error-reporting#límites-y-conservación).

**Archivos.** El tamaño máximo por archivo y el espacio total dependen de la cuota de tu aplicación. Consúltala con `files.getQuota()` (`files.get_quota()`): devuelve el espacio usado y el total en bytes, el máximo por archivo y el porcentaje de uso. Un archivo que supera el máximo o la cuota se rechaza con un error de validación. Los detalles, en [Archivos adjuntos](https://os.utilia.ai/docs/developers/sdk/files).

**Configuración.** El tiempo de espera (`timeout`), los intentos (`retryAttempts`, `retry_attempts`) y la espera máxima ante un 429 (`maxRetryAfterMs`, `max_retry_after`) se cambian al crear el cliente. Si el servidor no dice cuánto esperar, el SDK espera 0,5 s, 1 s, 2 s… (`0,5 × 2^intento`). Desde la 4.6.0, un `POST` sin clave de idempotencia no se repite. Más detalle en [Configurar el cliente](https://os.utilia.ai/docs/developers/sdk/configuration#reintentos).
