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

Anular una Factura

Cómo cancelar una factura CERTIFIED ante la SAT usando POST /v1/invoices/:id/cancel.

Anular una Factura

En Guatemala, anular una factura electrónica es un proceso formal ante la SAT. QAPI expone POST /v1/invoices/:id/cancel para ejecutarlo.

Solo facturas CERTIFIED se pueden anular

Solo se pueden cancelar facturas en estado CERTIFIED. Intentar cancelar una factura PENDING, PROCESSING, FAILED o CANCELLED retorna 409 INVALID_STATUS.

Antes de anular

Considera si realmente necesitas anular o si es más apropiado emitir una Nota de Crédito (NCRE) para ajustes parciales. La anulación es permanente e irreversible.

CasoAcción recomendada
Error total en la facturaAnulación (cancel)
Devolución parcialNota de Crédito (NCRE)
Ajuste de precioNota de Débito (NDEB) o Nota de Crédito

Cómo cancelar

Verifica el estado actual

Asegúrate de que la factura está en estado CERTIFIED:

curl https://api.dtevia.com.gt/v1/invoices/inv_01jxyz \
  -H "Authorization: Bearer qapi_live_xxxxxxxxxxxx"
const res = await fetch('https://api.dtevia.com.gt/v1/invoices/inv_01jxyz', {
  headers: { Authorization: 'Bearer qapi_live_xxxxxxxxxxxx' },
});
const invoice = await res.json();

if (invoice.status !== 'CERTIFIED') {
  throw new Error(`No se puede cancelar: estado actual es ${invoice.status}`);
}
res = httpx.get(
    "https://api.dtevia.com.gt/v1/invoices/inv_01jxyz",
    headers={"Authorization": "Bearer qapi_live_xxxxxxxxxxxx"},
)
invoice = res.json()

if invoice["status"] != "CERTIFIED":
    raise ValueError(f"No se puede cancelar: estado es {invoice['status']}")

Envía la solicitud de cancelación

El body requiere dos campos obligatorios:

CampoTipoReglas
motivostringMínimo 5 caracteres
fecha_anulacionstringISO 8601 con timezone Guatemala: YYYY-MM-DDTHH:mm:ss-06:00

El offset -06:00 es obligatorio

fecha_anulacion debe terminar exactamente en -06:00 (hora de Guatemala). Enviar Z, otro offset u omitir el campo retorna 422 VALIDATION_ERROR.

curl -X POST https://api.dtevia.com.gt/v1/invoices/inv_01jxyz/cancel \
  -H "Authorization: Bearer qapi_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "motivo": "Error en datos del receptor",
    "fecha_anulacion": "2026-06-09T10:30:00-06:00"
  }'
const res = await fetch('https://api.dtevia.com.gt/v1/invoices/inv_01jxyz/cancel', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer qapi_live_xxxxxxxxxxxx',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    motivo: 'Error en datos del receptor',
    fecha_anulacion: '2026-06-09T10:30:00-06:00',
  }),
});

const invoice = await res.json();
// invoice.status === "CANCELLED"
const { data } = await axios.post(
  'https://api.dtevia.com.gt/v1/invoices/inv_01jxyz/cancel',
  {
    motivo: 'Error en datos del receptor',
    fecha_anulacion: '2026-06-09T10:30:00-06:00',
  },
  { headers: { Authorization: 'Bearer qapi_live_xxxxxxxxxxxx' } }
);
// data.status === "CANCELLED"
res = httpx.post(
    "https://api.dtevia.com.gt/v1/invoices/inv_01jxyz/cancel",
    json={
        "motivo": "Error en datos del receptor",
        "fecha_anulacion": "2026-06-09T10:30:00-06:00",
    },
    headers={"Authorization": "Bearer qapi_live_xxxxxxxxxxxx"},
)
invoice = res.json()
# invoice["status"] == "CANCELLED"

Verifica la cancelación

La respuesta exitosa es 200 con un resumen de la anulación:

{
  "id": "inv_01jxyz",
  "status": "CANCELLED",
  "cancelled_at": "2026-06-09T16:30:00.000Z"
}

Si necesitas el objeto completo de la factura (con uuid_sat, totales, etc.), consúltalo con GET /v1/invoices/:id.

El PDF después de anular

La factura anulada conserva sus datos de certificación (uuid_sat, serie, numero_dte, pdf_url) — la autorización de la SAT existió y es parte del rastro fiscal del documento. El pdf_url sigue siendo accesible: el certificador re-renderiza la representación gráfica con la marca de agua "ANULADO", que es el comprobante visual de la anulación.

Errores comunes

Código HTTPErrorCausa
409INVALID_STATUSLa factura no está en estado CERTIFIED
404NOT_FOUNDEl id no existe o no pertenece a tu tenant
422VALIDATION_ERRORFalta motivo (mín. 5 caracteres) o fecha_anulacion no tiene el formato YYYY-MM-DDTHH:mm:ss-06:00
502CERTIFIER_ERROREl certificador rechazó la anulación

Webhook de cancelación

Si tienes webhooks configurados, recibirás el evento invoice.cancelled cuando la cancelación se complete:

{
  "event": "invoice.cancelled",
  "data": {
    "id": "inv_01jxyz",
    "status": "CANCELLED",
    "test": false
  }
}

El payload de este evento trae id, status y test (true si la factura se emitió con el certificador MOCK —sandbox o cuenta sin activar—, false en emisiones reales); la fecha de anulación está en cancelled_at vía GET /v1/invoices/:id.

Ver la guía de Webhooks para la configuración completa.

On this page