Errores frecuentes al integrar
Los diez errores que más se repiten en las primeras integraciones con la API externa de UTILIA OS y cómo evitarlos.
Actualizado el . 4 min de lectura
Incluido en todos los planes. Ver precios
Esta página recoge los errores que más se repiten en las primeras integraciones, cada uno con su forma correcta. Léela antes de llevar tu integración a producción.
1. No mandes userId en el cuerpo cuando el endpoint lo espera en la consulta
Muchos endpoints de tickets, facturas y tarjetas esperan userId en la consulta, no en el cuerpo. Mandarlo mal no da un error claro: la API descarta el campo que no espera y la petición falla después con USER_NOT_FOUND.
Dónde aplica:
GET /tickets/{id}(query)GET /tickets(query)GET /tickets/unread-count(query)POST /tickets/{id}/messages(en la consulta, no en el cuerpo)PATCH /tickets/{id}/close(query)PATCH /tickets/{id}/reopen(query)GET /invoices/{id}(query)GET /invoices/{id}/pdf(query)DELETE /payment-methods/{pmId}(query)
Mal:
POST /api/external/v1/tickets/abc-123/messages
Content-Type: application/json
{ "userId": "usr-4821", "content": "..." }Bien:
POST /api/external/v1/tickets/abc-123/messages?userId=usr-4821
Content-Type: application/json
{ "content": "..." }La lista completa está en Dónde va cada parámetro.
2. No mandes campos extra dentro de context
context (en POST /tickets) admite solo url, appVersion, browserInfo y customData. Cualquier otro campo se descarta sin aviso: crees que envías datos que en realidad se pierden. Si necesitas más, mételos dentro de context.customData.
Mal:
{
"user": { "externalId": "usr-1" },
"title": "...",
"description": "...",
"context": {
"url": "https://miapp.com",
"userPlan": "premium",
"abVariant": "B"
}
}userPlan y abVariant no son campos de context: la petición se acepta, pero nunca llegan al ticket.
Bien:
{
"user": { "externalId": "usr-1" },
"title": "...",
"description": "...",
"context": {
"url": "https://miapp.com",
"customData": {
"userPlan": "premium",
"abVariant": "B"
}
}
}Si los datos son del usuario y no del momento del ticket, usa user.metadata, que se guarda con el usuario, en lugar de context.customData, que solo vale para ese ticket.
3. No pintes el objeto de error en tu interfaz
Cuando un endpoint falla, la respuesta es un objeto JSON con la forma:
{
"statusCode": 400,
"message": "Tipo de archivo no permitido: image/bmp...",
"error": "BAD_REQUEST",
"errorCode": "FILE_INVALID_MIME",
"timestamp": "2026-02-10T14:23:45.123Z",
"requestId": "550e8400-..."
}Si pasas el objeto entero como hijo en JSX (<div>{error.response.data}</div>) o a toast.error(error.response.data), React lanza «Objects are not valid as a React child» y la pantalla se rompe.
Mal:
toast.error(error.response.data); // React falla y la pantalla se rompeBien:
toast.error(error.response.data?.message ?? 'Error desconocido');
// y guarda error.response.data?.requestId para soporteUsa errorCode para decidir en tu código y message para el texto que ve la persona.
4. No confundas /errors/report con /tickets/client-errors
Son dos endpoints con propósitos distintos, y se confunden porque los dos reciben errores.
| Endpoint | Para qué sirve |
|---|---|
POST /errors/report | Errores del servidor de tu aplicación. Acepta module, file, line, function, endpoint y method. Los errores iguales se agrupan. |
POST /tickets/client-errors | Errores del navegador (SDK o widget). Acepta code, component, url y userAgent, en lotes. |
Si mandas un error del navegador a /errors/report, component, url y userAgent se descartan sin aviso. Al revés, si mandas un error de servidor a /tickets/client-errors, pierdes module, file y line.
Detalle en Endpoints de errores de tu aplicación y Endpoint de errores del cliente.
5. No mandes cuerpo a PATCH /close ni a /reopen
Estos endpoints solo usan el identificador de la ruta ({id}) y userId en la consulta. Si envías cuerpo, se ignora: los datos no se guardan y tampoco hay error.
Si quieres dejar el motivo del cierre, envíalo como mensaje antes de cerrar (ver el ejemplo).
6. No ignores USER_NOT_FOUND
404 USER_NOT_FOUND significa que el endpoint necesita un usuario externo ya identificado y no lo está. Pasa en casi todos los endpoints que reciben userId suelto (no un objeto user).
Mal: capturar el error y mostrar «Algo salió mal» sin hacer nada más.
Bien:
- Llama a
POST /users/identifyantes del primer endpoint que lo necesite y guarda elexternalIden tu sistema. - Con el SDK de JavaScript o Python, identifica al usuario con
sdk.users.identify(...)antes de operar sobre él. El SDK no lo hace por su cuenta:
const sdk = new UtiliaSDK({
baseURL: 'https://os.utilia.ai/api',
apiKey: 'utl_xxx',
});
await sdk.users.identify({
externalId: 'user_42',
email: 'ada@example.com',
name: 'Ada Lovelace',
});Más en la configuración del SDK de JavaScript y del SDK de Python.
7. No reintentes en errores 4xx
Reintenta solo en 429, 5xx o fallos de red pasajeros (tiempo agotado, conexión cortada). Reintentar un 400, 401, 403 o 404 no arregla nada y gasta tu cupo de peticiones.
Usa espera exponencial (1, 2, 4 y 8 segundos) con algo de azar y un tope de cuatro intentos.
8. No pongas la clave de API en el navegador
La clave es secreta. Si llamas a la API externa desde el navegador, cualquiera puede verla en las herramientas de desarrollo. Llama desde tu servidor o, si necesitas hacerlo desde la web, pasa por un intermediario tuyo que añada la clave.
9. Envía tu propio X-Request-Id
La cabecera X-Request-Id une tu petición con los registros de UTILIA OS. Si no la envías, el servidor genera una y te la devuelve; si la envías tú, puedes seguir la misma petición en tus registros desde el principio. Genera un identificador por petición, guárdalo en tus registros y dáselo a soporte cuando algo falle.
X-Api-Key: utl_xxx
X-Request-Id: 7c2d5b30-f8a1-4d6e-9c3b-1234567890ab10. No decidas con el campo error
error ("BAD_REQUEST", "NOT_FOUND"...) solo repite el estado HTTP. Para decidir en tu código, usa errorCode ("USER_NOT_FOUND", "TICKET_CANNOT_REOPEN"...). El catálogo completo está en Errores, identificador de petición y reintentos.