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

# Errores

> Formato de errores, codigos HTTP y errores SIAT comunes en la API de CUCU.

## Formato de respuesta

Todas las respuestas de la API siguen el formato estandar `ApiResponse`. Esto aplica tanto
para respuestas exitosas como para errores, lo que facilita el manejo uniforme en tu codigo:

**Respuesta exitosa**

```json theme={"system"}
{
  "success": true,
  "message": "Operacion exitosa",
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "invoiceNumber": 1,
    "cuf": "2872F7294502332E637FABFBC3654EA82202AD48E0969F14EBEB8AF74",
    "state": "VALIDATED"
  },
  "error": null,
  "timestamp": "2026-02-11T12:00:00"
}
```

**Error de validacion**

```json theme={"system"}
{
  "success": false,
  "message": "createInvoice.request.clientBusinessName: no debe estar vacio",
  "error": {
    "code": "VALIDATION_ERROR"
  },
  "timestamp": "2026-02-11T12:00:00"
}
```

**Error SIAT**

```json theme={"system"}
{
  "success": false,
  "message": "CUFD NO VIGENTE O NO EXISTE",
  "error": {
    "code": "SIAT_ERROR"
  },
  "timestamp": "2026-02-11T12:00:00"
}
```

**Error interno**

```json theme={"system"}
{
  "success": false,
  "message": "Error interno del servidor",
  "error": {
    "code": "INTERNAL_ERROR",
    "details": "ref=3f9c2a1e-7d3b-4c8e-9a61-2b7e4f9d0c13"
  },
  "timestamp": "2026-02-11T12:00:00"
}
```

### Campos de una respuesta de error

Los campos nulos no se serializan: en un error no vas a ver `data`, y en un exito no vas a ver `error`.

| Campo | Tipo | Siempre presente | Descripcion |
| - | - | - | - |
| `success` | boolean | Si | `false` |
| `message` | string | Si | Mensaje legible con la causa del error. En `VALIDATION_ERROR` lista cada campo invalido como `ruta.campo: motivo`, separados por `;` |
| `error.code` | string | Casi siempre | Codigo interno de CUCU (ver tabla abajo). Algunas respuestas de los filtros de autenticacion y de rate limiting traen solo `success` y `message` (ver [Autenticacion](https://docs.cucu.bo/authentication#errores-de-autenticacion)) |
| `error.details` | string | No | Solo en `INTERNAL_ERROR`: la referencia `ref=<uuid>` para reportar a soporte |
| `timestamp` | string | Si | Fecha/hora de la respuesta |

> [!NOTE]
>
> El objeto `error` **no** trae `field`, `siatCode` ni `siatDescription`. El campo con problema
> viene en `message` (en los `VALIDATION_ERROR`), y el texto del SIAT viene tambien en `message`
> (en los `SIAT_ERROR`). El codigo numerico del SIAT de una factura ya emitida se consulta en
> `siatCodeState` / `siatDescription` de la factura, no en el error.

***

## Codigos HTTP

| Codigo | Nombre | Cuando ocurre | Accion recomendada |
| - | - | - | - |
| `200` | OK | Request exitoso (consultas, anulaciones) | Procesar la respuesta normalmente |
| `201` | Created | Recurso creado exitosamente (factura emitida) | Guardar el `cuf` y las URLs de descarga |
| `400` | Bad Request | Datos invalidos, faltantes o con formato incorrecto | Leer `message` y corregir el request |
| `401` | Unauthorized | API Key invalida, faltante o revocada | Verificar el header `X-API-Key` |
| `403` | Forbidden | La key no tiene permisos para este recurso | Verificar permisos de la API Key |
| `404` | Not Found | Recurso no encontrado (factura, POS, evento, lote, empresa). Un recurso de **otro tenant** responde igual que uno inexistente | Verificar el ID o CUF usado |
| `409` | Conflict | Estado conflictivo (ej: anular factura ya anulada) | Verificar el estado actual del recurso |
| `413` | Payload Too Large | El archivo supera el tamano maximo (hoy: logo de empresa, 2 MiB) | Reducir el archivo |
| `422` | Unprocessable Entity | Datos validos en formato pero no en negocio (incluye `Idempotency-Key` ya usada o de una factura anulada) | Revisar reglas de negocio SIAT o usar otra clave |
| `429` | Too Many Requests | Limite de requests por API Key, cupo de reenvio de correo, o demasiadas credenciales invalidas desde tu IP | Esperar lo que indica `Retry-After` (segundos) y reintentar |
| `500` | Internal Server Error | Error inesperado del servidor | Reportar a soporte con la referencia `error.details` (`ref=<uuid>`) |
| `502` | Bad Gateway | El SIAT respondio y **rechazo** la operacion (`SIAT_ERROR`): credencial/token del contribuyente invalido, no autorizado, rechazo de negocio | No reintentar: corregir la causa (ver `error.details`) |
| `503` | Service Unavailable | SIAT caido, timeout o degradado (`SIAT_UNAVAILABLE`) | Reintentar con backoff; al emitir, CUCU pasa a contingencia sola |

***

## Codigos de error internos

| Codigo | Descripcion | HTTP | Retriable |
| - | - | - | - |
| `VALIDATION_ERROR` | Datos del request no pasan validacion (`message` lista cada campo) | 400 | No (corregir datos) |
| `BAD_REQUEST` | Argumento invalido detectado por la logica de negocio | 400 | No (corregir datos) |
| `UNAUTHORIZED` | Falla de autenticacion | 401 | No (verificar key) |
| `FORBIDDEN` | Sin permisos para el recurso | 403 | No (verificar permisos) |
| `NOT_FOUND` | Recurso no existe | 404 | No (verificar ID) |
| `CONFLICT` | Estado conflictivo | 409 | No (verificar estado) |
| `SECTOR_NOT_SUPPORTED` | El documento sector no esta implementado en este ambiente | 422 | No (usar un sector disponible) |
| `CREDIT_DEBIT_NOTE_NOT_ALLOWED` | El sector de la factura original no admite notas de credito-debito (p. ej. el `3`, exportacion: no genera IVA que ajustar) | 422 | No (anular y reemitir) |
| `SIAT_ERROR` | El SIAT esta de pie y rechazo la operacion | 502 (400 en `POST`/`GET /api/v1/siat/cuis` cuando el SIAT responde `transaccion=false`) | No (corregir la causa) |
| `SIAT_UNAVAILABLE` | SIAT no responde, timeout o degradado | 503 | Si (backoff o contingencia) |
| `DOCUMENT_IN_PREPARATION` | El documento pedido se esta generando en este momento (factura recien emitida) | 503 + `Retry-After` | Si (esperar los segundos indicados; el siguiente pedido lo encuentra listo) |
| `DOCUMENT_NOT_AVAILABLE` | La factura no tiene documento: estado `REJECTED`, `PENDING` o `ERROR`, o sin XML | 409 | No (revisar el estado de la factura) |
| `DOCUMENT_LINK_GONE` | El link temporal `/api/v1/documents/t/{token}` no existe, vencio o agoto sus usos | 410 | No (pedir otro con `descargarDocumento`) |
| `PUBLIC_DOCUMENTS_GONE` | Los links publicos `/api/v1/public/invoice/{cuf}/*` estan cerrados para este emisor; `error.details` = `doc_url=...` | 410 | No (migrar a [`documents`](https://docs.cucu.bo/api/get-invoice#descargar-documentos)) |
| `RATE_LIMITED` | Cupo de reenvio de factura por correo superado (`POST /invoices/{id}/resend-email`) | 429 + `Retry-After` | Si (esperar) |
| `IDEMPOTENCY_KEY_REUSED` | La `Idempotency-Key` ya se uso con otro cuerpo | 422 | No (usar otra clave) |
| `IDEMPOTENCY_KEY_CANCELLED` | La `Idempotency-Key` pertenece a una factura **anulada**: no se devuelve como emitida ni se emite otra con esa clave | 422 | No (facturar de nuevo con una clave nueva) |
| `IDEMPOTENCY_IN_PROGRESS` | Otra request con la misma `Idempotency-Key` sigue en el SIN | 409 + `Retry-After` | Si (misma clave) |
| `LOGO_INVALIDO` | El logo subido no es PNG, JPEG ni WebP (se verifica el contenido, no el `Content-Type`) | 400 | No |
| `LOGO_DEMASIADO_GRANDE` | El logo supera el tamano maximo (2 MiB) | 413 | No |
| `INTERNAL_ERROR` | Error inesperado del servidor. `error.details` trae `ref=<uuid>`: el detalle real queda solo en los logs de CUCU bajo esa referencia | 500 | Si (reintentar; si persiste, reportar la `ref`) |

> [!NOTE]
>
> **Limites que responden 429 sin `error.code`.** El limite general por API Key (60 requests por
> minuto con rafaga de 10, o el de tu plan) y el freno a credenciales invalidas (mas de 20 llaves
> invalidas por minuto desde una misma IP) responden `429` con `Retry-After` y un cuerpo minimo
> con `message`, sin `error.code`. En ambos casos la accion es la misma: esperar `Retry-After`
> segundos. Detalle y forma exacta en [Autenticacion](https://docs.cucu.bo/authentication#errores-de-autenticacion).

### `SIAT_ERROR` (502) vs `SIAT_UNAVAILABLE` (503)

CUCU separa "el SIAT dijo que no" de "el SIAT no esta":

| Lo que paso con el SIAT | HTTP | `error.code` | Reintentar |
| - | - | - | - |
| Respondio con un SOAP fault de **credencial o negocio** (ej: `API KEY NO VALIDO`, `NO AUTORIZADO`, token vencido, XML rechazado) | `502` | `SIAT_ERROR` | **No** — reintentar devuelve lo mismo. Revisar el token delegado del contribuyente / su configuracion |
| Rechazo la operacion con mensajes de negocio (anulacion, CUFD, registro de punto de venta) | `502` | `SIAT_ERROR` | No |
| Rechazo la solicitud o consulta de CUIS (`POST`/`GET /api/v1/siat/cuis`) con `transaccion=false` | `400` | `SIAT_ERROR` | No |
| No respondio: timeout, conexion rechazada, respuesta corrupta o un fault generico de un SIAT degradado (ej: `Internal Server Error`) | `503` | `SIAT_UNAVAILABLE` | Si, con backoff |
| El circuit breaker del endpoint SIAT esta abierto (varias caidas seguidas) | `503` | `SIAT_UNAVAILABLE` | Si, en unos minutos |

> [!WARNING]
>
> **Cambio de contrato (2026-09-26):** un SOAP fault de credencial como `API KEY NO VALIDO`
> antes llegaba como `503 SIAT_UNAVAILABLE` (y los clientes lo reintentaban sin fin). Ahora
> llega como `502 SIAT_ERROR` y **no debe reintentarse**: es un problema del token delegado
> del contribuyente, no una caida del SIAT. Si tu integracion reintentaba ante `503`, no
> cambies nada; si trataba `502` como reintentable, sacalo de la lista.

***

## Errores SIAT

Cuando el SIAT (Sistema Integrado de Administracion Tributaria) rechaza una operacion,
la respuesta incluye el codigo SIAT original junto con la descripcion y una solucion sugerida.

> **Errores de codigos (CUIS, CUFD, CUF)**
>
> Estos errores estan relacionados con los codigos de seguridad que el SIAT requiere para
> autorizar emisiones de facturas.
>
> | Codigo SIAT | Descripcion | Solucion |
> | - | - | - |
> | `980` | CUFD no vigente o no existe | El CUFD expira cada 24 horas. Regenerar con `POST /api/v1/codes/cufd`. CUCU lo renueva automaticamente, pero si emites muchas facturas al dia puede necesitar renovacion manual. |
> | `985` | CUF duplicado | La factura ya fue emitida con este CUF. No reenviar. Consultar el estado con `GET /api/v1/invoices/{id}`. |
> | `1024` | CUIS no valido o expirado | El CUIS cambio o fue revocado. Solicitar nuevo CUIS con `POST /api/v1/siat/cuis`. Esto ocurre si se reregistro el sistema en el SIN. |
> | `1025` | CUIS no corresponde al punto de venta | El CUIS fue generado para otro punto de venta. Generar CUIS para el POS correcto. |
> | `1026` | NIT del emisor no corresponde | El NIT del emisor registrado en SIAT no coincide con el de la empresa. Verificar la configuracion de la empresa. |
>
> > [!TIP]
> >
> > CUCU renueva automaticamente el CUFD cada 24 horas. Si ves el error `980` de forma recurrente,
> > contacta a soporte para verificar que el cron de renovacion esta funcionando correctamente.

> **Errores de datos de factura**
>
> Estos errores ocurren cuando los datos enviados en la factura no cumplen con las reglas del SIAT.
>
> | Codigo SIAT | Descripcion | Solucion |
> | - | - | - |
> | `986` | NIT del cliente no valido | Verificar el NIT del cliente con `POST /api/v1/codes/nit`. El SIAT valida que el NIT exista y este activo. |
> | `990` | Datos de factura invalidos | Revisar campos requeridos, formatos y codigos del catalogo SIN. El `details` del error indicara el campo especifico. |
> | `991` | Codigo de producto SIN no valido | El `codeProductSin` no existe en el catalogo SIN. Consultar codigos validos con `POST /api/v1/siat/sync-products`. |
> | `992` | Actividad economica no valida | El `activityEconomic` no esta registrado para la empresa. Verificar actividades registradas en el SIN. |
> | `993` | Codigo de unidad de medida no valido | El `unitMeasure` no existe en el catalogo SIN. Consultar unidades con `POST /api/v1/siat/sync-parameters`. |
> | `994` | Codigo de metodo de pago no valido | El `paymentMethodCode` no es valido. Valores comunes: `1` (efectivo), `2` (tarjeta), `6` (transferencia). |
> | `996` | Monto total no coincide con detalle | La suma de los items no coincide con el total calculado. Verificar cantidades y precios unitarios. |
> | `997` | Tipo de documento no valido | El `clientDocumentType` no es valido. Valores: `1` (CI), `2` (CEX), `3` (pasaporte), `4` (OD), `5` (NIT). |
>
> > [!TIP]
> >
> > Para validar datos antes de emitir, usa los endpoints de sincronizacion SIAT
> > (`/api/v1/siat/sync-*`) para obtener los catalogos actualizados de productos,
> > actividades economicas, unidades de medida y metodos de pago.

> **Errores de certificado y autorizacion**
>
> Errores relacionados con el certificado digital y la autorizacion del sistema ante el SIN.
>
> | Codigo SIAT | Descripcion | Solucion |
> | - | - | - |
> | `1003` | Certificado digital expirado | Renovar el certificado digital en el portal del SIN (impuestos.gob.bo). El certificado tiene una vigencia de 1 ano. |
> | `1004` | Certificado digital no valido | El certificado no corresponde al NIT o fue revocado. Verificar en el SIN. |
> | `1005` | Firma digital invalida | Error en el proceso de firma. Contactar soporte para verificar la configuracion del certificado. |
> | `1010` | Token de delegacion expirado | El token de delegacion caduco. Renovar en el portal del SIN. CUCU notificara por email antes del vencimiento. |
> | `1011` | Sistema no registrado | El sistema no esta registrado en SIAT. Completar el registro en el portal del SIN. |
>
> > [!WARNING]
> >
> > Los errores de certificado requieren accion en el portal del SIN (impuestos.gob.bo).
> > No se pueden resolver via API. Planifica la renovacion del certificado con anticipacion
> > (al menos 15 dias antes del vencimiento).

> **Errores de disponibilidad del SIAT**
>
> Estos errores indican que el SIAT no esta disponible o tiene problemas de servicio.
>
> | Codigo SIAT | Descripcion | Solucion |
> | - | - | - |
> | `99999` | Servicio no disponible | El SIAT esta completamente caido. Activar modo contingencia con `POST /api/v1/contingency/start`. Las facturas se almacenan localmente y se sincronizan cuando el SIAT vuelva. |
> | `90009` | Servicio temporalmente no disponible | El SIAT tiene problemas parciales. Reintentar en 2-5 minutos. Si persiste, activar contingencia. |
> | `90010` | Timeout de conexion | La conexion al SIAT excedio el tiempo limite. Reintentar. Si es recurrente, puede indicar problemas de red. |
> | `90011` | Error de comunicacion | Fallo en la comunicacion SOAP con el SIAT. Reintentar. Si persiste por mas de 10 minutos, activar contingencia. |
>
> > [!TIP]
> >
> > CUCU detecta automaticamente cuando el SIAT no esta disponible y sugiere activar el modo
> > contingencia. En contingencia, las facturas se emiten con CUF offline y se sincronizan
> > automaticamente cuando el SIAT vuelve a estar disponible.

> **Errores de anulacion**
>
> Errores que ocurren al intentar anular una factura.
>
> | Codigo SIAT | Descripcion | Solucion |
> | - | - | - |
> | `2001` | Factura no encontrada en SIAT | El CUF no existe en el SIAT. Verificar que la factura fue emitida correctamente. |
> | `2002` | Factura ya anulada | La factura ya fue anulada previamente. No se puede anular dos veces. |
> | `2003` | Plazo de anulacion vencido | Supero el plazo maximo para anular (segun normativa SIN). Contactar al SIN directamente. |
> | `2004` | Motivo de anulacion no valido | El codigo de motivo de anulacion no es valido. Valores: `1` (error de datos), `2` (devolucion), `3` (nota de credito-debito). |

***

## Manejo de errores en codigo

### Patron basico

```javascript JavaScript theme={"system"}
async function emitirFactura(invoiceData) {
  try {
    const response = await fetch('https://sandbox.cucu.bo/api/v1/invoices', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.CUCU_API_KEY
      },
      body: JSON.stringify(invoiceData)
    });

    if (response.status === 429) {
      // Limite por API Key, cupo de reenvio o freno por credenciales invalidas: esperar Retry-After
      const retryAfter = Number(response.headers.get('Retry-After') || 5);
      console.warn(`Rate limited. Reintentando en ${retryAfter}s...`);
      await sleep(retryAfter * 1000);
      return emitirFactura(invoiceData); // Reintentar (misma Idempotency-Key)
    }

    const result = await response.json();

    if (!result.success) {
      const code = result.error?.code;
      const message = result.message;

      switch (code) {
        case 'VALIDATION_ERROR':
          // message lista cada campo invalido: "ruta.campo: motivo; ..."
          console.error(`Datos invalidos: ${message}`);
          break;

        case 'SIAT_ERROR':
          // 502: el SIAT rechazo (credencial, negocio). No reintentar a ciegas.
          console.error(`El SIAT rechazo la operacion: ${message}`);
          break;

        case 'SIAT_UNAVAILABLE':
          // 503: SIAT caido o degradado. Reintentable.
          console.warn('SIAT no disponible. Activando contingencia...');
          return emitirEnContingencia(invoiceData);

        case 'IDEMPOTENCY_KEY_CANCELLED':
          // 422: la venta con esa clave se anulo. Facturar de nuevo requiere una clave nueva.
          console.error(`Clave de una factura anulada: ${message}`);
          break;

        case 'INTERNAL_ERROR':
          // 500: guardar la referencia para soporte (error.details = "ref=<uuid>")
          console.error(`Error interno, referencia: ${result.error.details}`);
          break;

        default:
          console.error(`Error [${code ?? response.status}]: ${message}`);
      }

      return null;
    }

    console.log('Factura emitida:', result.data.cuf);
    return result.data;

  } catch (networkError) {
    console.error('Error de red:', networkError.message);
    // Decidir si reintentar o activar contingencia
    return null;
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

### Patron con reintentos y backoff exponencial

```javascript JavaScript theme={"system"}
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      const result = await response.json();

      // No reintentar errores de validacion, autenticacion ni rechazos del SIAT
      // (SIAT_ERROR 502 incluye "API KEY NO VALIDO": reintentar devuelve lo mismo)
      if (!result.success && ['VALIDATION_ERROR', 'BAD_REQUEST', 'NOT_FOUND', 'CONFLICT', 'SIAT_ERROR', 'IDEMPOTENCY_KEY_REUSED', 'IDEMPOTENCY_KEY_CANCELLED'].includes(result.error?.code)) {
        return result; // Error no retriable
      }

      // Reintentar errores de servidor y SIAT caido (SIAT_UNAVAILABLE 503)
      if (!result.success && ['SIAT_UNAVAILABLE', 'DOCUMENT_IN_PREPARATION', 'INTERNAL_ERROR', 'RATE_LIMITED', 'IDEMPOTENCY_IN_PROGRESS'].includes(result.error?.code)) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        console.warn(`Error retriable [${result.error.code}]. Reintentando en ${delay}ms...`);
        await sleep(delay);
        continue;
      }

      return result; // Exito u otro error

    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      const delay = Math.pow(2, attempt) * 1000;
      console.warn(`Error de red. Reintentando en ${delay}ms...`);
      await sleep(delay);
    }
  }
}
```

***

## Resolucion de errores con AI

CUCU incluye un endpoint de IA que puede analizar errores del SIAT y sugerir soluciones
automaticamente:

```bash theme={"system"}
curl -X POST https://sandbox.cucu.bo/api/v1/ai/resolve-error \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "errorCode": "SIAT_ERROR",
    "siatCode": 990,
    "siatDescription": "DATOS DE FACTURA INVALIDOS",
    "context": "Intentando emitir factura con NIT 12345678"
  }'
```

La respuesta se basa en la normativa SIAT indexada y en errores ya resueltos; detalle en [AI](https://docs.cucu.bo/api/ai.md).

***

## Checklist de debugging

Cuando algo falla, sigue este orden:

**1. Revisa el codigo HTTP**

El codigo HTTP te dice la categoria general del error. Los `4xx` son errores de tu lado
(datos, autenticacion, permisos). Los `5xx` son errores del servidor o del SIAT.

**2. Lee el campo error.code**

El codigo interno de CUCU te dice exactamente que tipo de error es. Usa las tablas de arriba
para determinar si es retriable o no.

**3. Lee message**

`message` tiene el texto legible. Si es un `VALIDATION_ERROR`, lista cada campo invalido con su
motivo (`ruta.campo: motivo; ...`). Si es un `SIAT_ERROR`, trae el texto con el que el SIAT
rechazo la operacion.

**4. Si es un 500, guarda la referencia**

`error.details` trae `ref=<uuid>`. El mensaje interno (SQL, hosts, rutas) ya no viaja en la
respuesta: queda en los logs de CUCU bajo esa referencia. Mandala a soporte junto con el
`timestamp`.

**5. Si es un 429, respeta Retry-After**

Puede ser el limite por API Key, el cupo de reenvio de correo o el freno a credenciales invalidas
desde tu IP. En los tres casos el header `Retry-After` dice cuantos segundos esperar.

**6. Usa el endpoint de AI si no encuentras la solucion**

`POST /api/v1/ai/resolve-error` puede analizar el error y sugerir la solucion correcta
basandose en la documentacion del SIAT y errores previamente resueltos.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.