Factu API Documentación

Boleta simple

La venta de mostrador, sin identificar al comprador. Es lo normal en una bodega y vale hasta S/ 700.

POSThttps://cpe-dev.facturamovilai.me/v1/emisores/TU_RUC/comprobantes

Cabecera 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-3101",
  "precios_incluyen_igv": true,
  "cliente": {
    "tipo_documento": "SIN_DOCUMENTO"
  },
  "items": [
    {
      "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-3101",
    "precios_incluyen_igv": true,
    "cliente": {
      "tipo_documento": "SIN_DOCUMENTO"
    },
    "items": [
      {
        "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-3101",
        "precios_incluyen_igv": True,
        "cliente": {
            "tipo_documento": "SIN_DOCUMENTO",
        },
        "items": [
            {
                "descripcion": "Fideos",
                "cantidad": "3",
                "valor_unitario": "3.50",
            },
        ],
    },
)

print(respuesta.status_code, respuesta.json())

Campos

CampoTipoObligatorioDescripción
tipoopciónSíBOLETA, FACTURA o NOTA_CREDITO. Es lo único que cambia entre los tres casos
Valores: FACTURA · BOLETA · NOTA_CREDITO
serietextoSí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_documentoopciónSí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 RUC
Valores: RUC · DNI · SIN_DOCUMENTO
items[].descripciontextoSíNombre del producto o servicio, tal como debe verse en el comprobante
items[].cantidaddecimalSíCuántas unidades se venden, en la unidad indicada. Admite decimales (2.5 kg). Debe ser mayor que cero
items[].valor_unitariodecimalSí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[].unidadopciónNoEn 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 bien
Lista completa: GET /v1/unidades
Por defecto: UNIDAD
items[].afectacionopciónNoQué 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 debes
Valores: GRAVADO · EXONERADO · INAFECTO
Por defecto: GRAVADO
referencia_externatextoNoEl 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_igvbooleanoNotrue 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 quitarle
Por defecto: false
total_esperadodecimalNoDí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_emisionfechaNoPor 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-36",
  "emisor": "TU_RUC",
  "ambiente": "dev",
  "tipo": "BOLETA",
  "serie": "B001",
  "numero": 36,
  "estado": "ACEPTADO",
  "mensaje": "El documento fue enviado correctamente y aceptado por SUNAT.",
  "total": "10.50",
  "referencia_externa": "venta-3101",
  "pdf_factuapi_a4_url": "https://cpe-dev.facturamovilai.me/v1/pdf/<token>",
  "desglose": {
    "gravado": "8.90",
    "exonerado": "0.00",
    "inafecto": "0.00",
    "igv": "1.60",
    "total": "10.50",
    "items": [
      {
        "descripcion": "Fideos",
        "valor_unitario": "2.966102",
        "igv": "1.60",
        "total": "10.50"
      }
    ]
  }
}

El mismo pedido con "serie": "X001"

{
  "error": "DatosInvalidos",
  "mensaje": "La serie debe tener 4 caracteres y empezar con B"
}

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.