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· alcancecomprobantes.anular. - Solo comprobantes
aceptadouobservado. Uno que aún no está en SUNAT, que ya está anulado o que no se puede anular responde 409ya_anuladoo 422no_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.