Saltar al contenido
Tarifa anual desde S/ 37 al mes + IGV. Pide tu demo sin costo. Reserva tu demo
Sistema Facturación Perú
Menú del portal

Guías por comprobante

Anular comprobantes: comunicación de baja y resumen de anulación

Anula facturas, boletas y sus notas por API con una sola llamada: el sistema elige la comunicación de baja o el resumen de anulación y te da el ticket de SUNAT.

Actualizado el

Con una sola llamada anulas cualquier comprobante aceptado. El sistema elige el camino que pide SUNAT:

Comprobante Cómo se anula ante SUNAT
Factura y sus notas Comunicación de baja
Boleta y sus notas Resumen diario de anulación
  • Endpoint: POST /comprobantes/{id}/anular · alcance comprobantes.anular.
  • Solo comprobantes aceptado u observado. Uno que aún no está en SUNAT, que ya está anulado o que no se puede anular responde 409 ya_anulado o 422 no_anulable.

Pide la anulación

curl -X POST https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes/1521/anular \
  -H "Authorization: Bearer $SFP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: anular-1521" \
  -d '{"motivo": "Error en el precio"}'
<?php
$ch = curl_init('https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes/1521/anular');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('SFP_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: anular-1521',
    ],
    CURLOPT_POSTFIELDS => json_encode(['motivo' => 'Error en el precio']),
]);
$anulacion = json_decode(curl_exec($ch), true)['data'];   // ['tipo' => 'baja', 'ticket' => '…', …]
const res = await fetch('https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes/1521/anular', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SFP_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'anular-1521',
  },
  body: JSON.stringify({ motivo: 'Error en el precio' }),
});
const { data: anulacion } = await res.json(); // { tipo: 'baja', ticket: '…', … }
r = requests.post(
    "https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes/1521/anular",
    json={"motivo": "Error en el precio"},
    headers={"Authorization": f"Bearer {os.environ['SFP_API_KEY']}", "Idempotency-Key": "anular-1521"},
    timeout=90,
)
anulacion = r.json()["data"]

El motivo va de 3 a 100 caracteres. La respuesta es 202: la anulación se envió y SUNAT la procesa por ticket.

{
  "success": true,
  "data": {
    "tipo": "baja",
    "ticket": "1730646000123",
    "external_id": "a1b2c3d4-…",
    "estado": { "codigo": "03", "clave": "enviado", "descripcion": "Enviado" },
    "consultar": "https://pruebas.sistemafacturacionperu.com/api/v1/anulaciones/1730646000123"
  }
}

Usar como Idempotency-Key algo como anular-<id> es buena idea: si repites la llamada por un corte de red, no se pide dos veces.

Consulta el resultado

GET /anulaciones/{ticket} consulta el ticket en SUNAT en ese momento y devuelve el estado de la baja o del resumen:

{
  "success": true,
  "data": {
    "tipo": "baja",
    "ticket": "1730646000123",
    "estado": { "codigo": "05", "clave": "aceptado", "descripcion": "Aceptado" },
    "sunat": { "codigo": "0", "descripcion": "La Comunicacion de baja RA-20261104-1, ha sido aceptada", "notas": [] }
  }
}

Después, GET /comprobantes/{id} muestra el comprobante anulado.

Mejor todavía: con un webhook te llegan resumen.procesado (SUNAT procesó la baja o el resumen) y comprobante.anulado (el comprobante quedó anulado), sin preguntar. El sistema también consulta solo las bajas que quedaron sin respuesta.

Mientras tanto

El comprobante queda por_anular hasta que SUNAT responde. Si SUNAT rechaza la baja (por ejemplo, porque pasó su plazo), el comprobante no queda anulado: en ese caso emite una nota de crédito.

Portal para desarrolladores · ¿Algo no está claro o te falta un ejemplo? Escríbenos y lo mejoramos.