Saltar al contenido
Tarifa anual desde S/ 37 al mes + IGV. Pide tu demo sin costo. Reserva tu demo
Sistema Facturación Perú
Menú del portal

Guías por comprobante

Factura electrónica por API

JSON mínimo para emitir una factura electrónica SUNAT por API, al contado o a crédito, desde una cotización, y cómo enviarla por correo o WhatsApp.

Actualizado el

La factura (tipo 01) es para vender a empresas y negocios con RUC. Se envía a SUNAT en la misma llamada (según la configuración de la empresa), así que la respuesta ya suele traer aceptado u observado y la respuesta de SUNAT.

  • Endpoint: POST /comprobantes · alcance comprobantes.emitir.
  • Serie: una de factura de tu local (F001…). "numero_documento": "#" toma el siguiente correlativo.

JSON mínimo

Una factura de S/ 1.180 (40 bolsas de cemento a S/ 29,50 con IGV) pagada en efectivo:

{
  "serie_documento": "F001",
  "numero_documento": "#",
  "fecha_de_emision": "2026-11-04",
  "hora_de_emision": "10:15:00",
  "codigo_tipo_operacion": "0101",
  "codigo_tipo_documento": "01",
  "codigo_tipo_moneda": "PEN",
  "datos_del_cliente_o_receptor": {
    "codigo_tipo_documento_identidad": "6",
    "numero_documento": "20601234567",
    "apellidos_y_nombres_o_razon_social": "CONSTRUCTORA ANDINA S.A.C.",
    "codigo_pais": "PE",
    "ubigeo": "150101",
    "direccion": "Av. Lima 123",
    "correo_electronico": "compras@andina.pe"
  },
  "totales": {
    "total_operaciones_gravadas": 1000,
    "total_igv": 180,
    "total_impuestos": 180,
    "total_valor": 1000,
    "total_venta": 1180
  },
  "items": [{
    "codigo_interno": "CEM-42",
    "descripcion": "Cemento 42.5 kg",
    "unidad_de_medida": "NIU",
    "cantidad": 40,
    "valor_unitario": 25,
    "codigo_tipo_precio": "01",
    "precio_unitario": 29.5,
    "codigo_tipo_afectacion_igv": "10",
    "total_base_igv": 1000,
    "porcentaje_igv": 18,
    "total_igv": 180,
    "total_impuestos": 180,
    "total_valor_item": 1000,
    "total_item": 1180
  }],
  "pagos": [{ "codigo_metodo_pago": "01", "codigo_destino_pago": "cash", "monto": 1180 }]
}

Guárdalo como factura.json y envíalo:

curl -X POST https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes \
  -H "Authorization: Bearer $SFP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: factura-$(uuidgen)" \
  -d @factura.json
<?php
$ch = curl_init('https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_TIMEOUT => 90,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('SFP_API_KEY'),
        'Content-Type: application/json',
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
    ],
    CURLOPT_POSTFIELDS => file_get_contents('factura.json'),
]);
$factura = json_decode(curl_exec($ch), true);
import { readFile } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';

const res = await fetch('https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SFP_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': randomUUID(),
  },
  body: await readFile('factura.json', 'utf8'),
});
const factura = await res.json();
import json, os, uuid, requests

r = requests.post(
    "https://pruebas.sistemafacturacionperu.com/api/v1/comprobantes",
    json=json.load(open("factura.json", encoding="utf-8")),
    headers={"Authorization": f"Bearer {os.environ['SFP_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    timeout=90,
)
factura = r.json()

Respuesta (201):

{
  "success": true,
  "data": {
    "id": 1521,
    "external_id": "9a0b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d",
    "tipo": "01",
    "numero_completo": "F001-120",
    "total": 1180,
    "estado": { "codigo": "05", "clave": "aceptado", "descripcion": "Aceptado" },
    "sunat": { "codigo": "0", "descripcion": "La Factura numero F001-120, ha sido aceptada", "notas": [] },
    "enlaces": { "pdf": "…/comprobantes/1521/pdf", "xml": "…/comprobantes/1521/xml", "cdr": "…/comprobantes/1521/cdr" }
  }
}

Los campos que más se equivocan

Campo Qué poner
datos_del_cliente_o_receptor.codigo_tipo_documento_identidad 6 (RUC). Una factura a un DNI la rechaza SUNAT (2017: el receptor debe tener RUC).
codigo_tipo_operacion 0101 venta interna; 1001 si está sujeta a detracción; 0200 exportación.
codigo_tipo_afectacion_igv 10 gravado, 20 exonerado, 30 inafecto, 40 exportación.
valor_unitario / precio_unitario Sin IGV / con IGV. Con codigo_tipo_precio: "01" (precio unitario).
unidad_de_medida Catálogo SUNAT: NIU (unidad, bienes), ZZ (servicios), KGM, MTR, LTR…
pagos[].codigo_metodo_pago 01 efectivo; los demás son los medios de pago de tu empresa.
pagos[].codigo_destino_pago cash (caja) o el id de una cuenta de cobro.

Factura a crédito

Con "codigo_condicion_de_pago": "02" y las cuotas. SUNAT pide al menos una cuota en una venta a crédito (3249):

{
  "codigo_condicion_de_pago": "02",
  "cuotas": [
    { "fecha": "2026-12-04", "codigo_tipo_moneda": "PEN", "monto": 590 },
    { "fecha": "2027-01-04", "codigo_tipo_moneda": "PEN", "monto": 590 }
  ]
}

Sin pagos, la factura queda en Por cobrar. Registra cada pago después con POST /comprobantes/{id}/cobros:

{ "fecha": "2026-12-04", "monto": 590, "medio": "005", "cuenta_cobro_id": 2, "referencia": "OP-778812" }

medio es el código del medio de pago de tu empresa; si no es efectivo, va con la cuenta de cobro (cuenta_cobro_id, un banco o billetera de ese tipo) y el número de operación. Sin cuenta, el cobro entra a la caja. Un cobro mayor que el saldo responde 422 cobro_excede_saldo. GET /comprobantes/{id}/cobros dice cuánto falta.

Facturar una cotización, un pedido o una nota de venta

Agrega origen y el comprobante queda enlazado, como en la web:

{ "origen": { "tipo": "cotizacion", "id": 15 } }

Enviarla a tu cliente

POST /comprobantes/{id}/enviar la manda por correo o por el WhatsApp de tu empresa (la línea que conectaste en Canales › WhatsApp; nunca por una línea de la plataforma):

{ "canal": "whatsapp", "destino": "987654321" }
{ "canal": "correo", "destino": "compras@andina.pe, contabilidad@andina.pe" }

Si el proveedor de correo o de WhatsApp falla, responde 502 envio_fallido.

Si SUNAT no responde

Si al emitir SUNAT no contestó, la factura queda registrado. Reenvíala con POST /comprobantes/{id}/reenviar-sunat (con su propia Idempotency-Key). Una que ya está aceptada responde 409 no_reenviable.

Errores frecuentes

code Qué pasó
serie_invalida La serie no es de factura o no es de tu local.
regimen_no_permite Tu régimen (Nuevo RUS) no emite facturas.
deuda_vencida El cliente tiene deuda vencida y tu empresa restringe la venta en ese caso.
producto_no_existe Con control de stock, el codigo_interno no existe: créalo con POST /productos.
detraccion_minimo La detracción no aplica a ese monto (casos especiales).
limite_plan Tu plan ya no permite emitir más este ciclo.

El resto está en el catálogo de errores.

Portal para desarrolladores · ¿Algo no está claro o te falta un ejemplo? Escríbenos y lo mejoramos.