Documentación de integración · emite factura, nota de crédito y nota de débito
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.
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).
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.
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 / Ruta | Para qué |
|---|---|
| GET /api/pe/admin/tenants | Lista los emisores dados de alta |
| POST /api/pe/admin/tenants/<id>/api-keys | Genera una API key adicional para un emisor |
Una sola vez por empresa, con su API key:
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>" }
Credenciales SOL de SUNAT (usuario = RUC + usuario SOL secundario). Se guardan cifradas.
{ "usuario": "20XXXXXXXXXUSUARIO", "clave": "<clave SOL>", "ambienteDefault": "produccion" }
{
"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" }]
}
{
"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": []
}
}
referencia, la API
no reemite: devuelve el comprobante ya enviado (con "idempotente": true).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.
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).
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": {...} }
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": [...] }
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": "..." } }
ACEPTADO. El CDR aceptado cubre a todas las boletas del resumen.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": [ ... ]
}
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.)
{
"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.
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.
{
"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.
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.
client_id/client_secret en SUNAT SOL (Menú SEE → Credenciales de API).{ "clientId": "<client_id de SUNAT>", "clientSecret": "<client_secret>" }
{
"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": {...} }
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.
Consulta el ticket de la GRE (como el resumen, pero por la API REST). 202 en proceso · 200 aceptado · 422 rechazado.
| Método / Ruta | Para qué |
|---|---|
| GET /api/pe/comprobantes disponible | Listado de comprobantes emitidos. Filtros: ?tipo=&estado=&desde=&hasta=&page=&limit= |
| GET /api/pe/comprobante/<clave>/xml disponible | XML firmado del comprobante |
| GET /api/pe/comprobante/<clave>/cdr disponible | CDR (constancia de SUNAT) |
| GET /api/pe/comprobante/<clave>/estado-sunat disponible | Reconsulta a SUNAT (getStatusCdr) y recupera el CDR. Solo producción. |
| GET /api/pe/estado disponible | Estado del servicio (público) |
La clave es el filename que devuelve la emisión (RUC-tipo-serie-número).
| Código | Significado |
|---|---|
| 10 | Gravado (IGV 18%) |
| 20 | Exonerado |
| 30 | Inafecto |
| 11 / 21 / 31 | Gratuito (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.
| Código | Documento |
|---|---|
| 6 | RUC |
| 1 | DNI |
| 4 | Carné de extranjería |
| 7 | Pasaporte |
| 0 | Sin documento |
| Código | Motivo |
|---|---|
| 01 | Anulación de la operación |
| 02 | Anulación por error en el RUC |
| 03 | Corrección por error en la descripción |
| 06 | Devolución total |
| 07 | Devolución por ítem |
| Código | Significado |
|---|---|
| 200 | Emitido y aceptado por SUNAT (CDR 0) |
| 202 | Resumen en proceso: el ticket aún no resuelve (reintente el GET) |
| 422 | SUNAT lo rechazó/observó (ver bloque sunat) |
| 400 | Datos inválidos (ver error y code) |
| 401 | API key inválida o ausente |
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).