Boleta con productos exonerados
El caso real de una bodega: menestras exoneradas y fideos gravados en la misma boleta. La afectación es del producto, no del comprobante.
https://cpe-dev.facturamovilai.me/v1/emisores/TU_RUC/comprobantesCabecera Authorization: Bearer TU_LLAVE. Es la misma llave en pruebas y en producción: lo que cambia es la dirección (cpe-dev o cpe-prod).
Solicitud
curl -X POST "https://cpe-dev.facturamovilai.me/v1/emisores/TU_RUC/comprobantes" \
-H "Authorization: Bearer TU_LLAVE" \
-H "Content-Type: application/json" \
-d '{
"tipo": "BOLETA",
"serie": "B001",
"referencia_externa": "venta-3103",
"precios_incluyen_igv": true,
"total_esperado": "20.50",
"cliente": {
"tipo_documento": "SIN_DOCUMENTO"
},
"items": [
{
"descripcion": "Lentejas",
"cantidad": "2",
"valor_unitario": "5.00",
"unidad": "KILOGRAMO",
"afectacion": "EXONERADO"
},
{
"descripcion": "Fideos",
"cantidad": "3",
"valor_unitario": "3.50"
}
]
}'const respuesta = await fetch("https://cpe-dev.facturamovilai.me/v1/emisores/TU_RUC/comprobantes", {
method: "POST",
headers: {
"Authorization": "Bearer TU_LLAVE",
"Content-Type": "application/json",
},
body: JSON.stringify({
"tipo": "BOLETA",
"serie": "B001",
"referencia_externa": "venta-3103",
"precios_incluyen_igv": true,
"total_esperado": "20.50",
"cliente": {
"tipo_documento": "SIN_DOCUMENTO"
},
"items": [
{
"descripcion": "Lentejas",
"cantidad": "2",
"valor_unitario": "5.00",
"unidad": "KILOGRAMO",
"afectacion": "EXONERADO"
},
{
"descripcion": "Fideos",
"cantidad": "3",
"valor_unitario": "3.50"
}
]
}),
});
console.log(respuesta.status, await respuesta.json());# pip install requests
import requests
respuesta = requests.post(
"https://cpe-dev.facturamovilai.me/v1/emisores/TU_RUC/comprobantes",
headers={"Authorization": "Bearer TU_LLAVE"},
json={
"tipo": "BOLETA",
"serie": "B001",
"referencia_externa": "venta-3103",
"precios_incluyen_igv": True,
"total_esperado": "20.50",
"cliente": {
"tipo_documento": "SIN_DOCUMENTO",
},
"items": [
{
"descripcion": "Lentejas",
"cantidad": "2",
"valor_unitario": "5.00",
"unidad": "KILOGRAMO",
"afectacion": "EXONERADO",
},
{
"descripcion": "Fideos",
"cantidad": "3",
"valor_unitario": "3.50",
},
],
},
)
print(respuesta.status_code, respuesta.json())Campos
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
tipo | opción | Sí | BOLETA, FACTURA o NOTA_CREDITO. Es lo único que cambia entre los tres casosValores: FACTURA · BOLETA · NOTA_CREDITO |
serie | texto | Sí | Cuatro caracteres: una boleta empieza con B (B001) y una factura con F (F001). En una nota de crédito es la serie del documento que anula, no una propia |
cliente.tipo_documento | opción | Sí | Cómo se identifica el comprador: DNI, RUC o SIN_DOCUMENTO. SIN_DOCUMENTO solo vale en una boleta de hasta S/ 700; una factura exige RUCValores: RUC · DNI · SIN_DOCUMENTO |
items[].descripcion | texto | Sí | Nombre del producto o servicio, tal como debe verse en el comprobante |
items[].cantidad | decimal | Sí | Cuántas unidades se venden, en la unidad indicada. Admite decimales (2.5 kg). Debe ser mayor que cero |
items[].valor_unitario | decimal | Sí | Precio de una unidad. Con precios_incluyen_igv: true es el precio de vitrina, con IGV; con false, el valor neto. No puede ser negativo |
items[].unidad | opción | No | En qué se vende esto (catálogo 03 de SUNAT). UNIDAD por defecto, que es vender de a una. Usa KILOGRAMO, LITRO, BOLSA, CAJA, DOCENA y demás cuando corresponda: va declarado en el comprobante, así que vender 2 kg de lentejas como 2 UNIDAD declara algo distinto de lo que vendiste. SERVICIO para lo que no es un bienLista completa: GET /v1/unidadesPor defecto: UNIDAD |
items[].afectacion | opción | No | Qué dice la ley del IGV de este producto. GRAVADO (18%) por defecto. EXONERADO para lo que no paga IGV -menestras, arroz, verduras, libros- e INAFECTO para exportaciones. Una bodega puede vender los dos en la misma boleta. Si lo marcas mal, el total igual cuadra pero declaras IGV que no debesValores: GRAVADO · EXONERADO · INAFECTOPor defecto: GRAVADO |
referencia_externa | texto | No | El id de esta venta en TU sistema. Mándalo siempre: es lo único que evita emitir dos comprobantes si reintentas. Repetirlo con los mismos datos devuelve el comprobante ya emitido; repetirlo con datos distintos responde 409 |
precios_incluyen_igv | booleano | No | true si mandas precios de vitrina -lo que cobra la caja- y nosotros quitamos el IGV con 6 decimales. false si mandas valores netos. Un item EXONERADO no se convierte: no hay IGV que quitarlePor defecto: false |
total_esperado | decimal | No | Dígito de control opcional: lo que tu caja dice que suma. Si no coincide con lo calculado, se rechaza con 422 y no se emite nada |
fecha_emision | fecha | No | Por defecto, hoy en hora de Perú. Se admite hasta 5 días atrás en una boleta y 3 en una factura; una fecha más vieja responde 422 |
Respuestas
{
"id": "BOLETA-B001-38",
"emisor": "TU_RUC",
"ambiente": "dev",
"tipo": "BOLETA",
"serie": "B001",
"numero": 38,
"estado": "ACEPTADO",
"mensaje": "El documento fue enviado correctamente y aceptado por SUNAT.",
"total": "20.50",
"referencia_externa": "venta-3103",
"pdf_factuapi_a4_url": "https://cpe-dev.facturamovilai.me/v1/pdf/<token>",
"desglose": {
"gravado": "8.90",
"exonerado": "10.00",
"inafecto": "0.00",
"igv": "1.60",
"total": "20.50",
"items": [
{
"descripcion": "Lentejas",
"valor_unitario": "5.00",
"igv": "0.00",
"total": "10.00"
},
{
"descripcion": "Fideos",
"valor_unitario": "2.966102",
"igv": "1.60",
"total": "10.50"
}
]
}
}El mismo pedido con "total_esperado": "99.00"
{
"error": "DatosInvalidos",
"mensaje": "El total calculado S/ 20.50 no coincide con el total esperado S/ 99.00"
}Con una llave que no existe
{
"detail": "Credencial invalida"
}Con un RUC que no es de tu cuenta
{
"error": "EmisorNoAutorizado",
"mensaje": "El emisor 20999999999 no pertenece a esta cuenta"
}Repetir venta-3101 con otros productos
{
"error": "ReferenciaReutilizada",
"mensaje": "La referencia 'venta-3101' ya se uso para un comprobante con otros datos"
}estado |
Significa |
|---|---|
ACEPTADO |
el comprobante existe ante SUNAT |
RECHAZADO |
SUNAT lo procesó y lo rechazó. Llega con 201, no con un error: lee mensaje |
PENDIENTE |
SUNAT sigue procesando. Consúltalo más tarde |
La respuesta puede traer campos adicionales a los que ves aquí: usa los de estos ejemplos.
⚠️ Marcar mal la afectación no cambia el total, pero sí lo que se declara. Si mandas un producto gravado como exonerado, el comprobante sale por el monto correcto y declara menos IGV del que debes. Ninguna validación automática ve ese error: depende de que tu catálogo tenga bien la afectación.