Desarrolladores
API de facturación electrónica SUNAT: tu primera factura en 5 minutos
Una API de facturación electrónica recibe tu comprobante en JSON, lo convierte al XML UBL 2.1 que pide SUNAT, lo firma, lo envía y te devuelve el resultado con el PDF. Así emites tu primera factura en el entorno demo, paso a paso y con código copiable.
Escrito y revisado por José Guillermo Santisteban Guerrero · · 4 min de lectura
En resumen
- Una API de facturación electrónica recibe el comprobante en JSON y se encarga del XML UBL 2.1, la firma digital, el envío a SUNAT y el PDF.
- Con Facturalo pruebas gratis en el entorno demo: las facturas van a SUNAT Beta sin certificado ni Clave SOL.
- La factura se envía con POST /documentos/enviar y el token en el header Authorization, sin la palabra Bearer.
- Si la respuesta dice ACEPTADA, ya tienes los enlaces al PDF, al XML y al CDR de SUNAT.
- Para reintentar sin duplicar, usa el header Idempotency-Key o la misma serie y correlativo.
Una API de facturación electrónica recibe los datos de tu comprobante en JSON y hace todo lo que SUNAT exige: arma el XML en formato UBL 2.1, lo firma con el certificado digital de la empresa, lo envía a SUNAT y te devuelve el resultado con el PDF, el XML y el CDR. Tu sistema solo hace una petición HTTP. En esta guía emites tu primera factura en el entorno demo de Facturalo, sin certificado ni Clave SOL.
Lo que necesitas
- Una cuenta gratuita en console.facturalo.pe.
- Tu empresa registrada en la consola: al escribir el RUC, la razón social, la dirección y el ubigeo se completan solos.
- El token de demo (
sk_test_…), que copias en Empresas → ⋯ → Tokens. Guárdalo en una variable de entorno de tu backend, nunca en el frontend.
En demo, las facturas y boletas se envían a SUNAT Beta firmadas con un certificado de pruebas. No tienen validez tributaria, pero SUNAT las valida igual que en producción.
1. Arma la factura en JSON
Con tipoIgv: "10" (gravado) y precioUnitario con IGV incluido, Facturalo calcula el valor unitario, el IGV y los totales. Guarda esto como factura.json:
{
"documento": {
"sunatTransaccion": "0101",
"tipoComprobante": "01",
"serie": "F001",
"correlativo": "1",
"fechaEmision": "2026-11-03",
"moneda": "PEN",
"formaPago": "CONTADO",
"cliente": {
"tipoDocumento": "6",
"numeroDocumento": "20000000001",
"denominacion": "EMPRESA S.A.C.",
"direccion": "AV. LIMA 123 - LIMA - LIMA - LIMA"
},
"items": [
{
"unidadMedida": "NIU",
"descripcion": "SERVICIO DE CONSULTORÍA",
"cantidad": 1,
"precioUnitario": 118,
"tipoIgv": "10"
}
]
}
}
Los códigos vienen de los catálogos de SUNAT: 01 es factura (tabla 10), 6 es RUC (tabla 2), 0101 es venta interna y NIU es unidad.
2. Envíala con una petición HTTP
Con curl:
curl -X POST https://api.facturalo.pe/api/v3/documentos/enviar \
-H "Authorization: sk_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: primera-factura-demo" \
-d @factura.json
El token va tal cual en Authorization, sin la palabra Bearer.
Con Node.js (SDK oficial, npm install @facturalo-pe/node):
import { readFileSync } from "node:fs";
import { FacturaloClient } from "@facturalo-pe/node";
const factura = JSON.parse(readFileSync("factura.json", "utf8"));
const facturalo = new FacturaloClient({ token: process.env.FACTURALO_TOKEN });
const envio = await facturalo.documentos.enviar(factura.documento, {
idempotencyKey: "primera-factura-demo",
});
console.log(envio.estado, envio.enlaces.pdf);
Con PHP (SDK oficial, composer require facturalo-pe/facturalo-php):
<?php
require 'vendor/autoload.php';
use Facturalo\FacturaloClient;
$factura = json_decode(file_get_contents('factura.json'), true);
$facturalo = new FacturaloClient(getenv('FACTURALO_TOKEN'));
$envio = $facturalo->documentos->enviar($factura['documento'], idempotencyKey: 'primera-factura-demo');
echo $envio['estado'], PHP_EOL;
Con Python (SDK oficial, pip install facturalo-pe):
import json, os
from facturalo import FacturaloClient
with open("factura.json", encoding="utf-8") as f:
factura = json.load(f)
facturalo = FacturaloClient(os.environ["FACTURALO_TOKEN"])
envio = facturalo.documentos.enviar(factura["documento"], idempotency_key="primera-factura-demo")
print(envio["estado"], envio.get("enlaces", {}).get("pdf"))
3. Lee la respuesta
En unos segundos llega el resultado de SUNAT:
{
"success": true,
"message": "La Factura numero F001-00000001 ha sido aceptada",
"data": {
"estado": "ACEPTADA",
"entorno": "demo",
"documento": { "serie": "F001", "correlativo": "00000001", "tipo": "FACTURA" },
"sunat": { "codigo": "0", "mensaje": "La Factura numero F001-00000001 ha sido aceptada" },
"enlaces": { "pdf": "…", "pdfTicket": "…", "xml": "…", "cdr": "…" }
}
}
- ACEPTADA: el comprobante es válido. En
enlacestienes el PDF (A4 y ticket), el XML firmado y el CDR de SUNAT para entregar a tu cliente. - RECHAZADA: el motivo está en
sunat.codigoysunat.mensaje. Corrige el dato y envía un comprobante nuevo con otro correlativo; uno rechazado no se anula.
Si prefieres no esperar, envía "webhook": true: la API responde al instante y el resultado de SUNAT llega a tu servidor por webhook firmado.
4. Reintentos sin duplicados
Las redes fallan. Si no recibiste respuesta, reenvía la misma petición con el mismo Idempotency-Key, o con la misma serie y correlativo: Facturalo no crea un segundo comprobante y te devuelve el resultado del primero. Más detalle en idempotencia.
5. Pasa a producción
Cuando todo funcione en demo, carga en la consola el certificado digital de la empresa y su usuario SOL, y cambia el token sk_test_ por el sk_live_. La URL de la API es la misma. Desde ahí, cada factura aceptada tiene validez tributaria.
Con la misma API emites boletas, notas de crédito y débito, guías de remisión, y consultas RUC, DNI y tipo de cambio. La referencia completa está en la documentación de la API y el resumen del producto en API de facturación electrónica.
