# Códigos de error (/es/api-reference/errors)

## Resumen

Los errores de la API de Bitelio siguen una forma consistente, diseñada para que puedas identificar el problema rápido. Cada error trae:

* Un **código de error legible por máquina** sobre el que tu código puede ramificar
* Un **mensaje legible por humanos** que describe el fallo
* Una **sugerencia** que apunta a la solución probable
* Un **ID de solicitud** para depuración y conversaciones de soporte
* **Detalles de validación a nivel de campo** cuando aplican

## Códigos de estado HTTP

### 200 OK

La solicitud se procesó correctamente.

### 201 Created

El recurso fue creado.

### 400 Bad Request

Algo está mal en el formato de la solicitud o en sus parámetros — los detalles del error indican qué.

### 401 Unauthorized

Falta la autenticación o no tuvo éxito. Verifica tu clave de API.

### 402 Payment Required

Se alcanzó un límite de facturación, o la operación requiere un plan superior. Acompaña a `BILLING_LIMIT_EXCEEDED` y `UPGRADE_REQUIRED`.

### 403 Forbidden

No tienes permitido tocar este recurso — revisa los permisos y el estado del proyecto. También se usa cuando aún falta verificar el correo de la cuenta (`EMAIL_VERIFICATION_REQUIRED`) o el proyecto fue deshabilitado (`PROJECT_DISABLED`).

### 404 Not Found

No existe ningún recurso con ese ID. Confirma que el ID sea correcto.

### 409 Conflict

La solicitud choca con el estado existente — p. ej., crear un contacto cuyo correo ya está registrado, o renombrarlo a un correo que ya está en uso. Acompaña a `CONFLICT`.

### 422 Unprocessable Entity

La validación rechazó la solicitud. El array `errors` enumera los campos problemáticos.

### 429 Too Many Requests

Superaste un límite de tasa. Espera antes de reintentar, o pasa a un plan superior.

### 500 Internal Server Error

Algo se rompió de nuestro lado. Contacta a soporte con el ID de la solicitud.

## Formato de respuesta de error

Todos los errores usan esta única estructura:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "statusCode": 422,
    "requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "errors": [
      {
        "field": "email",
        "message": "Invalid email",
        "code": "invalid_string"
      }
    ],
    "suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

### Campos de la respuesta

| Campo              | Tipo    | Descripción                                           |
| ------------------ | ------- | ----------------------------------------------------- |
| `success`          | boolean | En errores siempre es `false`                         |
| `error.code`       | string  | Identificador legible por máquina (catalogado abajo)  |
| `error.message`    | string  | Explicación en lenguaje llano de lo que falló         |
| `error.statusCode` | number  | El estado HTTP                                        |
| `error.requestId`  | string  | Identificador por solicitud para depuración           |
| `error.errors`     | array   | Desglose por campo (solo fallos de validación)        |
| `error.details`    | object  | Contexto adicional, cuando está disponible (opcional) |
| `error.suggestion` | string  | Una pista hacia la solución (opcional)                |
| `timestamp`        | string  | Cuándo ocurrió el error, en ISO 8601                  |

## Referencia de códigos de error

El campo `code` está presente en todos los errores para que puedas manejarlos programáticamente. El conjunto completo:

### Autenticación y autorización

| Código                        | Estado | Descripción                                                      |
| ----------------------------- | ------ | ---------------------------------------------------------------- |
| `UNAUTHORIZED`                | 401    | Falló la autenticación por un motivo no especificado             |
| `INVALID_CREDENTIALS`         | 401    | Los datos de inicio de sesión no coinciden                       |
| `MISSING_AUTH`                | 401    | No hay un encabezado Authorization utilizable en la solicitud    |
| `INVALID_API_KEY`             | 401    | Esa clave de API no existe o no es válida                        |
| `REVOKED_API_KEY`             | 401    | Esa clave era válida y se ha revocado: emite una nueva           |
| `EXPIRED_API_KEY`             | 401    | Esa clave pasó su fecha de expiración: emite una nueva           |
| `FORBIDDEN`                   | 403    | Esta acción no está permitida                                    |
| `PROJECT_ACCESS_DENIED`       | 403    | No tienes acceso a este proyecto                                 |
| `PROJECT_DISABLED`            | 403    | El proyecto está actualmente deshabilitado                       |
| `EMAIL_VERIFICATION_REQUIRED` | 403    | Verifica la dirección de correo de la cuenta antes de hacer esto |

### Errores de validación y de entrada

| Código                   | Estado | Descripción                                                        |
| ------------------------ | ------ | ------------------------------------------------------------------ |
| `BAD_REQUEST`            | 400    | Problema genérico con la solicitud                                 |
| `VALIDATION_ERROR`       | 422    | La validación rechazó la solicitud (con errores de campo adjuntos) |
| `INVALID_EMAIL`          | 422    | La dirección de correo no tiene un formato válido                  |
| `INVALID_REQUEST_BODY`   | 400    | El cuerpo no se pudo interpretar                                   |
| `MISSING_REQUIRED_FIELD` | 422    | No se proporcionó un campo obligatorio                             |

### Errores de recursos

| Código               | Estado | Descripción                                          |
| -------------------- | ------ | ---------------------------------------------------- |
| `RESOURCE_NOT_FOUND` | 404    | Ningún recurso coincide (genérico)                   |
| `CONTACT_NOT_FOUND`  | 404    | No existe ese contacto                               |
| `TEMPLATE_NOT_FOUND` | 404    | No existe esa plantilla                              |
| `CAMPAIGN_NOT_FOUND` | 404    | No existe esa campaña                                |
| `WORKFLOW_NOT_FOUND` | 404    | No existe ese workflow                               |
| `CONFLICT`           | 409    | Choca con el estado existente (p. ej., un duplicado) |

### Límites de tasa y facturación

| Código                   | Estado | Descripción                              |
| ------------------------ | ------ | ---------------------------------------- |
| `RATE_LIMIT_EXCEEDED`    | 429    | La tasa de solicitudes es demasiado alta |
| `BILLING_LIMIT_EXCEEDED` | 402    | Se alcanzó un límite de facturación      |
| `UPGRADE_REQUIRED`       | 402    | La función requiere un plan superior     |

### Errores de servidor

| Código                   | Estado | Descripción                                   |
| ------------------------ | ------ | --------------------------------------------- |
| `INTERNAL_SERVER_ERROR`  | 500    | Fallo inesperado de nuestro lado              |
| `DATABASE_ERROR`         | 500    | Una operación de base de datos no se completó |
| `EXTERNAL_SERVICE_ERROR` | 500    | Un servicio externo no estuvo disponible      |

## Ejemplos comunes de error

### Errores de autenticación

#### Clave de API inválida

```json
{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid secret API key. This endpoint requires a secret key (sk_*), not a public key.",
    "statusCode": 401,
    "requestId": "abc-123",
    "suggestion": "Verify your API key is correct and starts with \"sk_\" for secret keys or \"pk_\" for public keys."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Confirma la clave en sí y su tipo — los endpoints que requieren una clave secreta esperan un prefijo `sk_`, mientras que el registro de eventos usa claves `pk_`.

#### Falta el encabezado Authorization

```json
{
  "success": false,
  "error": {
    "code": "MISSING_AUTH",
    "message": "Authorization header is required",
    "statusCode": 401,
    "requestId": "abc-123",
    "suggestion": "Include an Authorization header with format: \"Authorization: Bearer YOUR_API_KEY\""
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Envía tu clave como token Bearer en el encabezado `Authorization`.

### Errores de validación

#### Formato de correo inválido

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "statusCode": 422,
    "requestId": "abc-123",
    "errors": [
      {
        "field": "email",
        "message": "Invalid email",
        "code": "invalid_string"
      }
    ],
    "suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Envía una dirección de correo bien formada. Revisa el array `errors` para ver exactamente qué campos fueron rechazados.

#### Faltan campos obligatorios

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "statusCode": 422,
    "requestId": "abc-123",
    "errors": [
      {
        "field": "event",
        "message": "Required",
        "code": "invalid_type"
      },
      {
        "field": "email",
        "message": "Required",
        "code": "invalid_type"
      }
    ],
    "suggestion": "Required fields are missing. Ensure all required fields are included in your request."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Añade todos los campos obligatorios al cuerpo de la solicitud.

### Errores de recursos

#### Plantilla no encontrada

```json
{
  "success": false,
  "error": {
    "code": "TEMPLATE_NOT_FOUND",
    "message": "Template with ID \"tpl_abc123\" was not found",
    "statusCode": 404,
    "requestId": "abc-123",
    "details": {
      "resource": "Template",
      "id": "tpl_abc123"
    },
    "suggestion": "Ensure the template ID is correct and belongs to your project. You can list available templates via the API."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Asegúrate de que el ID de la plantilla exista y pertenezca a tu proyecto.

### Límite de tasa

#### Límite de tasa superado

```json
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please try again later.",
    "statusCode": 429,
    "requestId": "abc-123",
    "suggestion": "You have exceeded the rate limit. Wait a moment before retrying, or upgrade your plan."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Solución**: Añade lógica de reintento con backoff exponencial. Si sueles chocar con el techo, un cambio de plan lo eleva.

## Formato de respuesta de éxito

Cuando una solicitud tiene éxito, la respuesta llega como `success: true` con un objeto `data`:

```json
{
  "success": true,
  "data": {
    "contact": "cnt_abc123",
    "event": "evt_xyz789",
    "timestamp": "2025-11-30T10:30:00.000Z"
  }
}
```

### Campos de la respuesta

| Campo     | Tipo    | Descripción                     |
| --------- | ------- | ------------------------------- |
| `success` | boolean | En éxito siempre es `true`      |
| `data`    | object  | Payload específico del endpoint |

## Manejo de errores en tu código

### Ejemplo en JavaScript/TypeScript

```typescript
try {
  const response = await fetch('https://api.bitelio.com/v1/track', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${publicKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      event: 'purchase',
      email: 'user@example.com'
    })
  });

  const data = await response.json();

  if (!data.success) {
    // Algo salió mal
    console.error(`Error [${data.error.code}]:`, data.error.message);

    // Muestra la solución sugerida por la API
    if (data.error.suggestion) {
      console.log('Suggestion:', data.error.suggestion);
    }

    // Conserva el ID de la solicitud para soporte
    console.log('Request ID:', data.error.requestId);

    // Ramifica según el código específico
    switch (data.error.code) {
      case 'VALIDATION_ERROR':
        // Imprime cada campo que falló
        data.error.errors?.forEach(err => {
          console.log(`${err.field}: ${err.message}`);
        });
        break;
      case 'INVALID_API_KEY':
        // Pide al usuario que revise su clave
        break;
      case 'REVOKED_API_KEY':
      case 'EXPIRED_API_KEY':
        // La clave ERA válida: rótala, en vez de pedir que busquen una errata
        break;
      case 'RATE_LIMIT_EXCEEDED':
        // Reintenta más tarde con backoff
        break;
    }

    return;
  }

  // Camino de éxito
  console.log('Event tracked:', data.data);
} catch (error) {
  console.error('Network error:', error);
}
```

### Ejemplo en Python

```python
import requests

response = requests.post(
    'https://api.bitelio.com/v1/track',
    headers={
        'Authorization': f'Bearer {public_key}',
        'Content-Type': 'application/json'
    },
    json={
        'event': 'purchase',
        'email': 'user@example.com'
    }
)

data = response.json()

if not data.get('success'):
    error = data['error']
    print(f"Error [{error['code']}]: {error['message']}")

    # Muestra la solución sugerida
    if 'suggestion' in error:
        print(f"Suggestion: {error['suggestion']}")

    # Conserva el ID de la solicitud
    print(f"Request ID: {error['requestId']}")

    # Imprime los fallos a nivel de campo
    if error['code'] == 'VALIDATION_ERROR':
        for err in error.get('errors', []):
            print(f"{err['field']}: {err['message']}")
else:
    print(f"Event tracked: {data['data']}")
```

## Guía de resolución de problemas

### Uso de los IDs de solicitud

Cada error viene con un `requestId` que sigue la solicitud a través de todo nuestro stack. Eso lo hace valioso en dos frentes:

**Para desarrolladores:**

* El ID queda grabado en cada línea de log de esa solicitud
* Busca por ID en tus logs y aparece de golpe cada entrada relacionada
* Sigue una solicitud a través de API → Base de datos → Cola → Worker

**Para soporte:**

1. Pega el ID de la solicitud en tu mensaje
2. Explica qué estabas intentando hacer
3. Adjunta la respuesta de error completa cuando puedas

**Ejemplo:** Dado el ID de solicitud `f47ac10b-58cc-4372-a567-0e02b2c3d479`, una búsqueda en los logs de nuestro lado revela:

* El cuerpo de la solicitud tal como llegó
* Las consultas de base de datos que se ejecutaron
* Cualquier job en segundo plano que haya generado
* El stack trace completo, si existe alguno

Eso significa que normalmente podemos diagnosticar el problema de inmediato, sin ida y vuelta pidiéndote más detalles.

#### Cómo encontrar los IDs de solicitud

El ID de solicitud aparece en tres lugares:

* **Respuestas de error**: el campo `error.requestId`
* **Encabezados de respuesta**: el encabezado `X-Request-ID` (presente también en éxitos)
* **Los logs de tu aplicación**: escribe el encabezado en tus propios logs para poder correlacionar

```javascript
// Captura el ID de la solicitud en tu propio logging
const response = await fetch('https://api.bitelio.com/v1/send', {
  // ... tu solicitud
});

const requestId = response.headers.get('X-Request-ID');
console.log('Request ID:', requestId);  // Guárdalo para correlación

const data = await response.json();
if (!data.success) {
  console.error('Error:', data.error.message);
  console.error('Request ID:', data.error.requestId);  // Coincide con el encabezado
}
```

### Problemas comunes y soluciones

import {Accordion, Accordions} from 'fumadocs-ui/components/accordion';

<Accordions type="single">
  <Accordion title="Problemas de autenticación (401) — INVALID_API_KEY o MISSING_AUTH">
    * Asegúrate de que la clave se pegó exactamente, sin espacios extra.
    * Confirma que el tipo de clave coincide con el endpoint (`sk_` secreta vs. `pk_` pública).
    * Comprueba que el encabezado `Authorization` use el formato de token Bearer.
    * Asegúrate de que la clave no haya sido rotada o revocada mientras tanto.
  </Accordion>

  <Accordion title="Problemas de validación (422) — VALIDATION_ERROR">
    * Lee el array `errors` — nombra cada campo que falla.
    * Confirma que no falte nada obligatorio.
    * Verifica el tipo de cada campo (strings entre comillas, números sin comillas).
    * Compara tu payload contra la referencia de la API.
  </Accordion>

  <Accordion title="Problemas de recurso no encontrado (404) — TEMPLATE_NOT_FOUND, CONTACT_NOT_FOUND, etc.">
    * Vuelve a comprobar el ID del recurso.
    * Confirma que pertenece a tu proyecto, no a otro.
    * Asegúrate de que no haya sido eliminado.
    * Lista los recursos vía la API para verificar los IDs que tienes.
  </Accordion>

  <Accordion title="Problemas de límite de tasa (429) — RATE_LIMIT_EXCEEDED">
    * Reintenta con backoff exponencial (1s, 2s, 4s, 8s entre intentos).
    * Reduce tu tasa de solicitudes.
    * Un plan superior eleva el techo si necesitas más throughput.
    * Prefiere los endpoints por lotes cuando existan.
  </Accordion>

  <Accordion title="Errores de servidor (500) — INTERNAL_SERVER_ERROR">
    * Anota el ID de la solicitud de la respuesta.
    * Espera un momento y luego reintenta.
    * Consulta [status.bitelio.com](https://status.bitelio.com) por incidentes en curso.
    * Si sigue ocurriendo, contacta a soporte e incluye el ID de la solicitud.
  </Accordion>
</Accordions>

### Buenas prácticas

1. **Revisa `success` primero**, antes de tocar el resto de la respuesta
2. **Registra los IDs de solicitud** para que la depuración y las solicitudes de soporte vayan más rápido
3. **Falla con elegancia**, traduciendo los errores en mensajes que tus usuarios entiendan
4. **Reintenta los fallos transitorios** usando backoff exponencial
5. **Vigila tus tasas de error** para que los problemas salgan a la luz temprano
6. **Ramifica según los códigos de error**, no solo según los estados HTTP, en tu manejo

## Obtener ayuda

¿Sigues atascado? Recorre esta lista:

1. Empieza por el campo `suggestion` del propio error
2. Compara tu solicitud contra la [Referencia de la API](/es/api-reference/overview)
3. Busca tu código de error exacto en esta documentación
4. Escribe a soporte e incluye tu ID de solicitud
5. Pregunta a la comunidad — otros desarrolladores a menudo se han topado con lo mismo
