Saltar a contenido

Errores

Formato canónico

Todos los errores 4xx/5xx siguen este shape:

{
  "error": "human readable message in Spanish neutral",
  "code": "MACHINE_READABLE_CODE",
  "details": { "...": "..." },
  "request_id": "01HZQ7K8..."
}

Campos:

Campo Tipo Notas
error string Mensaje en español neutro, listo para mostrar al usuario
code string UPPER_SNAKE_CASE estable, machine-readable
details object Opcional. Información extra (campo inválido, retry_after, etc.)
request_id string ULID/UUID para grep en logs

Status codes

Status Cuándo Ejemplo code
400 Body malformado o validation pre-request INVALID_BODY, VALIDATION_ERROR
401 Sin auth o token inválido/expirado INVALID_CREDENTIALS, INVALID_REFRESH_TOKEN
403 Auth válida pero falta scope/role FORBIDDEN, MISSING_SCOPE
404 Recurso no existe (o cross-tenant) NOT_FOUND
409 Conflicto idempotencia o estado IDEMPOTENCY_KEY_CONFLICT, ALREADY_EXISTS
422 Validation post-parse (negocio) VALIDATION_ERROR
429 Rate limit RATE_LIMIT_EXCEEDED
500 Error interno INTERNAL_ERROR
501 Endpoint not implemented yet NOT_IMPLEMENTED
503 Dependencia caída (DB/Redis) DEPENDENCY_UNAVAILABLE

Códigos comunes

Auth (/auth/*)

code Status Descripción
INVALID_CREDENTIALS 401 Login fallido (email o password)
ACCOUNT_LOCKED 401 5 intentos fallidos → bloqueo 15 min
ACCOUNT_INACTIVE 401 users.is_active = false
INVALID_REFRESH_TOKEN 401 Cookie/body no matchea o expiró

ARCO (/user/*)

code Status Descripción
DELETE_REQUEST_PENDING 409 Ya hay un request en curso (window 30d)
EXPORT_TOO_LARGE 422 Data > 100MB → procesar offline

MCP (mcp.wiservet.com)

code Status Descripción
INVALID_API_KEY 401 Prefix incorrecto, revocada, o expirada
MISSING_SCOPE 403 El tool requiere un scope no asignado
RATE_LIMIT_EXCEEDED 429 Daily o burst limit
SQL_BLOCKED 400 Regex pre-execute detectó DML/DDL/escalación
STATEMENT_TIMEOUT 500 Query > 5s

Manejo recomendado en cliente

async function call<T>(req: Request): Promise<T> {
  const res = await fetch(req)
  if (res.ok) return res.json()
  const err = await res.json().catch(() => ({}))
  if (res.status === 401 && err.code === 'INVALID_REFRESH_TOKEN') {
    redirectToLogin()
  } else if (res.status === 429) {
    const retryAfter = parseInt(res.headers.get('Retry-After') ?? '60', 10)
    await sleep(retryAfter * 1000)
    return call(req) // reintento simple, ojo con loops
  }
  throw new ApiError(err.code ?? 'UNKNOWN', err.error ?? 'API error', err.request_id)
}

Reportar errores

Si encontrás un código no documentado o un comportamiento inconsistente: incluí request_id + body completo en soporte@wiservet.com.