API de Facturación Electrónica — Perú (SUNAT)

Documentación de integración · emite factura, nota de crédito y nota de débito

1. Introducción

Esta API emite facturas (01), notas de crédito (07) y notas de débito (08) electrónicas, las firma (XAdES) y las envía directamente a SUNAT (SEE del Contribuyente), devolviendo el CDR (constancia) real.

URL base: la de su instalación (por ejemplo https://su-dominio/). Mientras no se publique un dominio, se accede por el puerto interno de la instalación.

Principio clave — la API recibe, no decide. El sistema emisor arma el comprobante completo (serie, número, fecha, tipo de afectación del IGV, documento afectado) y la API lo transmite tal cual a SUNAT. La API no autogenera numeración, no asume impuestos por omisión y no inventa autorizaciones: todo "aceptado" proviene de SUNAT.

2. Autenticación

Cada empresa (emisor) tiene su propia API key. Se envía en cada petición:

X-API-Key: sk_pe_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

La API key la entrega el administrador de la instalación al dar de alta el emisor (ver §3).

3. Administración (dueño de la instalación)

Estos endpoints los usa quien administra la instalación, con la clave de administrador X-Admin-Key. Sirven para dar de alta a cada empresa emisora.

POST /api/pe/admin/tenants disponible

Da de alta un emisor y devuelve su API key una sola vez.

// cabecera: X-Admin-Key: <clave de administrador>
{ "ruc": "20XXXXXXXXX", "razonSocial": "MI EMPRESA S.A.C." }

// respuesta
{ "emisor": { "id": "...", "ruc": "20XXXXXXXXX", "razonSocial": "MI EMPRESA S.A.C." },
  "apiKey": "sk_pe_xxxxxxxxxxxx",
  "nota": "Guarde la API key: no se vuelve a mostrar." }
Método / RutaPara qué
GET /api/pe/admin/tenantsLista los emisores dados de alta
POST /api/pe/admin/tenants/<id>/api-keysGenera una API key adicional para un emisor

4. Puesta en marcha de un emisor

Una sola vez por empresa, con su API key:

POST /api/pe/certificado disponible

Carga el certificado de firma (.p12) del emisor. Se valida que el RUC del certificado coincida con el del emisor y se guarda cifrado.

{ "p12Base64": "<contenido del .p12 en base64>", "password": "<clave del certificado>" }

POST /api/pe/credenciales disponible

Credenciales SOL de SUNAT (usuario = RUC + usuario SOL secundario). Se guardan cifradas.

{ "usuario": "20XXXXXXXXXUSUARIO", "clave": "<clave SOL>", "ambienteDefault": "produccion" }

5. Emitir una factura

POST /api/pe/factura/emitir disponible

{
  "ambiente": "produccion",            // "beta" para pruebas
  "referencia": "venta-12345",          // su id de venta (idempotencia)
  "serie": "F001",                      // la letra define el tipo (F=factura)
  "numero": 123,                        // correlativo — lo asigna SU sistema
  "fechaEmision": "2026-10-20",
  "horaEmision": "10:30:00",
  "moneda": "PEN",
  "receptor": {
    "tipoDoc": "6",                     // 6=RUC, 1=DNI (ver tablas)
    "numDoc": "20123456789",
    "razonSocial": "EMPRESA CLIENTE S.A.",
    "direccion": "Av. Cliente 456"
  },
  "items": [{
    "descripcion": "Producto o servicio",
    "unidad": "NIU",
    "cantidad": 2,
    "valorUnitario": 50.00,             // sin IGV
    "precioUnitario": 59.00,            // con IGV
    "afectacionIgv": "10",              // 10=gravado, 20=exonerado, 30=inafecto
    "baseIgv": 100.00,
    "porcentajeIgv": 18,
    "igv": 18.00,
    "valorVenta": 100.00,
    "totalItem": 118.00
  }],
  "totales": {
    "gravadas": 100.00, "exoneradas": 0, "inafectas": 0, "gratuitas": 0,
    "igv": 18.00, "valorVenta": 100.00, "total": 118.00
  },
  "leyendas": [{ "code": "1000", "value": "SON CIENTO DIECIOCHO CON 00/100 SOLES" }]
}

Respuesta

{
  "exito": true,
  "data": {
    "numero": "F001-123",
    "filename": "20XXXXXXXXX-01-F001-123",
    "hash": "oN8iCSe4HUK..."
  },
  "links": {
    "xml": "/api/pe/comprobante/20XXXXXXXXX-01-F001-123/xml",
    "cdr": "/api/pe/comprobante/20XXXXXXXXX-01-F001-123/cdr"
  },
  "sunat": {                            // la respuesta de SUNAT (CDR), tal cual
    "code": "0",                        // 0 = aceptado · 2xxx/3xxx = rechazo · 4xxx = observado
    "description": "La Factura numero F001-123, ha sido aceptada",
    "notes": []
  }
}
Idempotencia: si reintenta la misma referencia, la API no reemite: devuelve el comprobante ya enviado (con "idempotente": true).

Casos especiales de factura (detracción, crédito, exportación) disponible

Campos opcionales que se agregan al mismo factura/emitir:

// Exportación: tipoOperacion 0200 + ítems con afectacionIgv "40" (IGV 0). Receptor NO RUC.
{ "tipoOperacion": "0200", "moneda": "USD", "receptor": { "tipoDoc": "7", "numDoc": "PAS123" }, ... }

// Detracción (SPOT): requiere tipoOperacion "1001" y leyenda "2006".
{ "tipoOperacion": "1001",
  "detraccion": { "codigoBienServicio": "037", "porcentaje": 12, "monto": 141.60,
                  "cuentaBanco": "<cuenta Banco de la Nación>", "medioPago": "001" },
  "leyendas": [ { "code": "2006", "value": "Operacion sujeta a detraccion" }, ... ], ... }

// Venta al crédito con cuotas (la fecha de cada cuota debe ser POSTERIOR a la emisión):
{ "pago": { "tipo": "Credito", "montoPendiente": 1180,
            "cuotas": [ { "numero": 1, "monto": 590, "fecha": "2026-10-21" } ] }, ... }

// ICBPER (bolsas plásticas): en el ítem bolsa, además de su IGV.
{ "items": [ { ..., "icbper": 1.50, "cantidadBolsas": 3, "tasaIcbper": 0.50 } ] }

// Descuento global (código 03 = no afecta base IGV; 02 = afecta base):
{ "descuentoGlobal": { "codigo": "03", "factor": 0.1, "monto": 10, "base": 100 },
  "totales": { ..., "taxInclusive": 118, "total": 108 } }

Validados en beta (CDR 0). La API arma el XML con lo que envía el emisor; el tipoOperacion y las leyendas obligatorias son responsabilidad del emisor.

6. Boletas y Resumen Diario (B2C)

Las boletas (03) no se envían una por una: se emiten (quedan pendientes), se agrupan en un Resumen Diario (RC) que se envía con sendSummary y devuelve un ticket; ese ticket se consulta hasta obtener el CDR. Así funciona el consumo masivo B2C (comercio, restaurantes, servicios).

POST /api/pe/boleta/emitir disponible

Igual que una factura, pero serie empieza con B y el receptor puede ser DNI (tipoDoc:"1") o consumidor final (tipoDoc:"0", numDoc:"00000000"). La boleta queda PENDIENTE hasta enviarse en el resumen.

{
  "ambiente": "produccion",
  "referencia": "venta-9001",
  "serie": "B001", "numero": 45,
  "fechaEmision": "2026-10-20", "moneda": "PEN",
  "receptor": { "tipoDoc": "1", "numDoc": "12345678", "razonSocial": "CLIENTE" },
  "items": [ ... ], "totales": { ... }, "leyendas": [ ... ]
}
// respuesta: { "exito": true, "estado": "PENDIENTE", "data": {...}, "links": {...} }

POST /api/pe/resumen-diario disponible

Agrupa y envía las boletas del día. El correlativo y las fechas los pone el emisor. Si se omite boletas, incluye todas las PENDIENTE de fechaReferencia.

{
  "ambiente": "produccion",
  "correlativo": 1,                     // correlativo del resumen del día (emisor)
  "fechaGeneracion": "2026-10-20",      // cuándo se envía
  "fechaReferencia": "2026-10-20",      // fecha de emisión de las boletas
  "boletas": ["B001-45", "B001-46"]     // opcional: ids a incluir
}
// respuesta: { "exito": true, "estado": "EN_PROCESO", "ticket": "1792...", "incluidas": [...] }

GET /api/pe/ticket/<ticket> disponible

Consulta el ticket (getStatus). Mientras SUNAT procesa devuelve 202 con estado:"EN_PROCESO"; al terminar, 200 con el CDR (estado:"ACEPTADO") o 422 si fue rechazado.

// 200
{ "exito": true, "estado": "ACEPTADO", "ticket": "1792...",
  "resumenId": "RC-20261020-1",
  "sunat": { "code": "0", "description": "El Resumen diario ... ha sido aceptado", "notes": [] },
  "links": { "xml": "...", "cdr": "..." } }
Flujo: emitir boletas → enviar resumen (obtiene ticket) → consultar ticket hasta ACEPTADO. El CDR aceptado cubre a todas las boletas del resumen.

7. Notas de crédito y débito

POST /api/pe/nota-credito/emitir  ·  POST /api/pe/nota-debito/emitir disponible

Igual que la factura, pero indicando el motivo y el documento afectado:

{
  "ambiente": "produccion",
  "serie": "FC01",                      // serie de la nota (empieza con F)
  "numero": 45,
  "fechaEmision": "2026-10-20",
  "moneda": "PEN",
  "tipoNota": "01",                     // motivo: catálogo 09 (NC) / 10 (ND)
  "motivo": "Anulación de la operación",
  "documentoAfectado": { "tipoDoc": "01", "serie": "F001", "numero": 123 },
  "receptor": { ... }, "items": [ ... ], "totales": { ... }, "leyendas": [ ... ]
}

8. Anular comprobantes — Comunicación de Baja

Da de baja ante SUNAT facturas (01) y notas (07/08) ya aceptadas. Usa el mismo flujo de ticket que el resumen diario. (Las boletas no se anulan aquí: se anulan en el resumen diario con condición de anulación.)

POST /api/pe/baja disponible

{
  "ambiente": "produccion",
  "correlativo": 1,
  "fechaGeneracion": "2026-10-20",   // fecha de la comunicación
  "fechaReferencia": "2026-10-20",   // fecha de emisión de los documentos a anular
  "documentos": [
    { "tipoDoc": "01", "serie": "F001", "numero": 123, "motivo": "ERROR EN LA OPERACION" }
  ]
}
// respuesta: { "exito": true, "estado": "EN_PROCESO", "ticket": "1792...", "anulados": ["F001-123"] }

El resultado se consulta en GET /api/pe/ticket/<ticket> (igual que el resumen). Plazo SUNAT: la baja de facturas se comunica dentro de los 7 días del día siguiente a la emisión.

9. Retención y Percepción (agentes designados)

Para empresas designadas por SUNAT como agentes de retención (20) o percepción (40). Envío directo con CDR inmediato (como la factura), pero con esquema propio.

POST /api/pe/retencion/emitir  ·  POST /api/pe/percepcion/emitir disponible

{
  "ambiente": "produccion",
  "serie": "R001", "numero": 1,          // P001 para percepción
  "fechaEmision": "2026-10-20", "moneda": "PEN",
  "regimen": "01", "tasa": 3,            // retención 3% · percepción 2% (regímenes SUNAT)
  "receptor": { "tipoDoc": "6", "numDoc": "20123456789", "razonSocial": "PROVEEDOR S.A." },
  "total": 30.00,                        // total retenido / percibido
  "totalPagado": 970.00,                 // total pagado (ret.) / cobrado (perc.)
  "documentos": [{
    "tipoDoc": "01", "serie": "F001", "numero": 500, "fechaEmision": "2026-10-15",
    "moneda": "PEN", "importeTotal": 1000.00,
    "numeroPago": 1, "importePago": 1000.00, "fechaPago": "2026-10-20",
    "importeConcepto": 30.00,            // monto retenido/percibido de ese documento
    "fecha": "2026-10-20", "importeNeto": 970.00
  }]
}
// respuesta: { "exito": true, "data": {...}, "sunat": { "code": "0", ... } }

El emisor (agente) necesita dirección en su alta (ubigeo, dirección, distrito). El CDR llega en la misma respuesta.

10. Guía de Remisión Electrónica (GRE)

Para el traslado de bienes (GRE remitente, tipo 09). A diferencia del resto, la GRE usa la API REST de SUNAT con OAuth (no el web service SOAP) y requiere credenciales API propias.

Importante: la GRE no tiene homologación/beta: los endpoints de SUNAT son de producción y cada guía aceptada es un documento legal real. El emisor debe generar su client_id/client_secret en SUNAT SOL (Menú SEE → Credenciales de API).

POST /api/pe/gre/credenciales disponible

{ "clientId": "<client_id de SUNAT>", "clientSecret": "<client_secret>" }

POST /api/pe/gre-remitente/emitir disponible

{
  "serie": "T001", "numero": 1,
  "fechaEmision": "2026-10-20", "horaEmision": "10:00:00",
  "destinatario": { "tipoDoc": "6", "numDoc": "20123456789", "razonSocial": "CLIENTE S.A." },
  "envio": {
    "motivo": "01",                       // motivo de traslado (catálogo 20): 01 venta, 04 entre establecimientos…
    "motivoDesc": "VENTA",
    "pesoTotal": 100, "unidadPeso": "KGM", "bultos": 2,
    "partida": { "ubigeo": "150101", "direccion": "AV. PARTIDA 123" },
    "llegada": { "ubigeo": "150203", "direccion": "AV. LLEGADA 456" }
  },
  "transporte": {
    "modalidad": "01",                    // 01 público · 02 privado
    "fechaInicio": "2026-10-20",
    "transportista": { "ruc": "20555555555", "razonSocial": "TRANSPORTES SAC" }
    // privado (02): "conductor": {tipoDoc,numDoc,nombres,apellidos,licencia}, "vehiculo": {placa}
  },
  "bienes": [{ "descripcion": "PRODUCTO", "cantidad": 10, "unidad": "NIU", "codigo": "P001" }]
}
// respuesta: { "exito": true, "estado": "EN_PROCESO", "ticket": "...", "data": {...} }

POST /api/pe/gre-transportista/emitir disponible

GRE del transportista (31): igual que la remitente pero el emisor es el transportista, identifica al remitente (dueño de los bienes) y siempre lleva conductor + vehiculo.

GET /api/pe/gre/ticket/<ticket> disponible

Consulta el ticket de la GRE (como el resumen, pero por la API REST). 202 en proceso · 200 aceptado · 422 rechazado.

11. Consultar y descargar

Método / RutaPara qué
GET /api/pe/comprobantes disponibleListado de comprobantes emitidos. Filtros: ?tipo=&estado=&desde=&hasta=&page=&limit=
GET /api/pe/comprobante/<clave>/xml disponibleXML firmado del comprobante
GET /api/pe/comprobante/<clave>/cdr disponibleCDR (constancia de SUNAT)
GET /api/pe/comprobante/<clave>/estado-sunat disponibleReconsulta a SUNAT (getStatusCdr) y recupera el CDR. Solo producción.
GET /api/pe/estado disponibleEstado del servicio (público)

La clave es el filename que devuelve la emisión (RUC-tipo-serie-número).

12. Tablas de referencia

Tipo de afectación del IGV (catálogo 07)

CódigoSignificado
10Gravado (IGV 18%)
20Exonerado
30Inafecto
11 / 21 / 31Gratuito (según corresponda)

Exonerado / inafecto / gratuito no son "IGV 0%": son afectaciones distintas. El emisor debe enviar el código; la API no asume ninguno.

Tipo de documento de identidad del receptor (catálogo 06)

CódigoDocumento
6RUC
1DNI
4Carné de extranjería
7Pasaporte
0Sin documento

Motivo de nota de crédito (catálogo 09)

CódigoMotivo
01Anulación de la operación
02Anulación por error en el RUC
03Corrección por error en la descripción
06Devolución total
07Devolución por ítem

13. Códigos de respuesta HTTP

CódigoSignificado
200Emitido y aceptado por SUNAT (CDR 0)
202Resumen en proceso: el ticket aún no resuelve (reintente el GET)
422SUNAT lo rechazó/observó (ver bloque sunat)
400Datos inválidos (ver error y code)
401API key inválida o ausente

14. Próximamente roadmap

Lo siguiente todavía no está disponible en esta versión. Se documenta aquí para que el equipo lo tenga en cuenta en la planificación, pero no debe asumirse operativo:

¿Falta algo que necesite el equipo? Indíquelo para priorizarlo en el roadmap.

Documento generado por el servicio sunat-api. Para pruebas use "ambiente": "beta" (homologación de SUNAT).