QAPI está en beta — la API puede cambiar sin previo aviso. Usa sandbox para pruebas.
QAPIQAPIDocs · by DTEVIA
Guías

Manejo de Errores

Catálogo completo de errores RFC 7807 de QAPI y cómo manejarlos en tu integración.

Manejo de Errores

QAPI usa el estándar RFC 7807 para todos los errores. Cada respuesta de error tiene una URL type que apunta a la documentación específica del error.

Formato de error

Ejemplo de respuesta

{
  "type": "https://docs.dtevia.com.gt/errors/validation-error",
  "title": "VALIDATION_ERROR",
  "status": 422,
  "detail": "Error de validación",
  "instance": "/v1/invoices",
  "invalid_params": [
    {
      "field": "items.0.impuestos.0.monto_impuesto",
      "message": "MontoImpuesto IVA inválido. Esperado ~12.00, recibido 10.00"
    }
  ]
}

Campos del objeto

CampoDescripción
typeURL de documentación del error. Siempre resuelve a una página en esta documentación.
titleCódigo del error en MAYÚSCULAS (constante, seguro para comparar en código)
statusCódigo HTTP
detailDescripción legible en español
instanceEndpoint que generó el error
invalid_paramsSolo en VALIDATION_ERROR. Lista de {field, message} con los campos que fallaron.

Catálogo de errores

error_code de factura ≠ errores HTTP

Una factura FAILED trae error_code: "SIGN_FAILED" o "CERT_FAILED" (también en el webhook invoice.failed). Son códigos del pipeline de certificación, un namespace distinto a los errores HTTP de este catálogo.


Manejo de errores en código

Por tipo de error

interface QapiError {
  type: string;
  title: string;
  status: number;
  detail: string;
  invalid_params?: { field: string; message: string }[];
}

async function emitirFactura(payload: InvoicePayload): Promise<Invoice> {
  const res = await fetch('https://api.dtevia.com.gt/v1/invoices', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.QAPI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });

  if (!res.ok) {
    const error: QapiError = await res.json();

    switch (error.status) {
      case 422:
        // Error de validación — muéstrale al usuario los campos inválidos
        const fields = error.invalid_params?.map(p => `${p.field}: ${p.message}`).join('\n');
        throw new ValidationError(`Factura inválida:\n${fields}`);

      case 429:
        // Rate limit — implementa backoff
        const retryAfter = res.headers.get('retry-after');
        throw new RateLimitError(`Reintenta en ${retryAfter}s`);

      case 401:
      case 403:
        throw new AuthError(error.detail);

      default:
        // Error del servidor — loggea el X-Request-ID para soporte
        const requestId = res.headers.get('x-request-id');
        throw new Error(`Error QAPI [${requestId}]: ${error.detail}`);
    }
  }

  return res.json();
}
import axios, { AxiosError } from 'axios';

async function emitirFactura(payload: InvoicePayload): Promise<Invoice> {
  try {
    const { data } = await axios.post(
      'https://api.dtevia.com.gt/v1/invoices',
      payload,
      { headers: { Authorization: `Bearer ${process.env.QAPI_API_KEY}` } }
    );
    return data;
  } catch (err) {
    if (!axios.isAxiosError(err) || !err.response) throw err;

    const error = err.response.data;

    if (error.status === 422 && error.invalid_params) {
      const fields = error.invalid_params.map((p: any) => `${p.field}: ${p.message}`);
      throw new Error(`Validación fallida: ${fields.join('; ')}`);
    }

    throw new Error(`QAPI Error ${error.status}: ${error.detail}`);
  }
}
class QapiValidationError(Exception):
    def __init__(self, invalid_params: list):
        self.invalid_params = invalid_params
        fields = ", ".join(f"{p['field']}: {p['message']}" for p in invalid_params)
        super().__init__(f"Validación fallida: {fields}")

def emitir_factura(payload: dict) -> dict:
    res = httpx.post(
        "https://api.dtevia.com.gt/v1/invoices",
        json=payload,
        headers={"Authorization": f"Bearer {QAPI_API_KEY}"},
    )

    if res.status_code != 202:
        error = res.json()

        if error["status"] == 422:
            raise QapiValidationError(error.get("invalid_params", []))

        raise Exception(f"QAPI Error {error['status']}: {error['detail']}")

    return res.json()

URLs de documentación de errores

Cómo funcionan los enlaces type

El campo type de cada error apunta a https://docs.dtevia.com.gt/errors/{slug}. Estas URLs están configuradas como redirects a páginas específicas de esta documentación que explican la causa y la solución de cada error.

Si tu backend genera un error con type: "https://docs.dtevia.com.gt/errors/validation-error", un desarrollador que haga click en esa URL llega directamente a la explicación de las 14 validaciones. No necesitas documentar los errores tú mismo.

On this page