API y webhooks

Para webs a medida y sistemas propios. Una llamada por venta; el resto (IGV, gratuitas, tipo de cambio, reintentos, envío al cliente, respaldo) lo hace BillerPro.

Necesitas el módulo «API y webhooks» (+ S/49/mes). Se activa en un clic desde Mi cuenta → Módulos, sobre cualquier plan. No hace falta para el plugin de WooCommerce, la app de Shopify, VTEX, el panel, la carga CSV, el POS, WhatsApp ni los pedidos de las apps de delivery: esos canales vienen con tu plan y usan la misma clave. Sin el módulo, POST /v1/emisiones con un origen propio y PUT /v1/webhook responden 403; las consultas y descargas siguen abiertas.

Lo esencial

Emitir un comprobante

POST /v1/emisiones

curl -X POST https://api.billerpro.store/v1/emisiones \
  -H "X-API-Key: bp_tu_clave" -H "Content-Type: application/json" \
  -d '{
    "origen": "miweb",
    "origen_id": "PED-10021",
    "moneda": "PEN",
    "receptor": { "numero_documento": "20601234567", "razon_social": "Cliente SAC",
                  "correo": "[email protected]", "direccion": "Av. Lima 123" },
    "lineas": [
      { "codigo": "ZAP-42", "descripcion": "Zapatillas", "cantidad": 1, "precio_unitario": "189.90" },
      { "codigo": "ENVIO", "descripcion": "Envío", "cantidad": 1, "precio_unitario": "15.00", "unidad": "ZZ" },
      { "codigo": "BOLSA", "descripcion": "Bolsa", "cantidad": 1, "precio_unitario": "0.10", "icbper_unitario": "0.50" },
      { "codigo": "REGALO", "descripcion": "Muestra crema", "cantidad": 1, "precio_unitario": "25.00", "afectacion_igv": "15" }
    ]
  }'

Campos del pedido

Campo Tipo Notas
origen string ≤30 Nombre de tu sistema. Por defecto woocommerce.
origen_id string ≤64 Obligatorio. Tu ID de venta. Clave de idempotencia.
fecha_emision YYYY-MM-DD Por defecto hoy.
moneda PEN | USD En USD, el tipo de cambio se toma de SUNAT si no lo envías.
tipo_cambio decimal Opcional. Solo si quieres fijarlo tú.
tipo_comprobante 01 | 03 Factura o boleta. Si falta, se deduce del documento.
receptor.numero_documento string ≤15 RUC (11) → factura; DNI (8) → boleta; vacío → "Cliente varios".
receptor.tipo_documento catálogo 06 1 DNI, 4 CE, 6 RUC, 7 pasaporte. Se deduce si falta.
receptor.razon_social string ≤250 Por defecto "Cliente varios".
receptor.correo email Si viene, el cliente recibe PDF y XML al aceptarse.
receptor.direccion string ≤250 Recomendado en facturas.
lineas[] 1 a 200 Ver abajo.
observacion string ≤250 Texto libre en el comprobante.

Campos de cada línea

Campo Tipo Notas
codigo string ≤30 Tu SKU.
descripcion string ≤250
cantidad decimal > 0
precio_unitario decimal ≥ 0 Con IGV. En gratuitas: precio de referencia (> 0).
unidad catálogo 03 NIU (unidad, defecto), ZZ (servicio), KGM, LTR…
afectacion_igv catálogo 07 10 gravado (defecto), 20 exonerado, 30 inafecto, 40 exportación, 15 bonificación, 11–16 otras gratuitas, 21/31 gratuitas exoneradas/inafectas.
icbper_unitario decimal Impuesto a la bolsa por unidad (2026: S/0.50).

Respuesta

201 Created (o 200 si ya existía):

{
  "id": "emi_01J…", "numero": "F001-00000042", "tipo_comprobante": "01",
  "serie": "F001", "correlativo": "42", "estado": "aceptado", "aceptado": true,
  "reintentando": false, "requiere_correccion": false,
  "total": "229.90", "moneda": "PEN", "intentos": 1,
  "fecha_emision": "2026-09-04T15:02:11Z", "vence_en": null, "horas_restantes": null,
  "ya_existia": false, "origen": "miweb", "mensaje": null
}

Estados: aceptado, aceptado_con_observacion, reintentando (SUNAT/PSE no respondió; reintentamos solos), rechazado (corrige el dato y vuelve a emitir con otro origen_id o anula), anulado, error.

Errores

Código Significado
401 Clave ausente, inválida o revocada.
403 Cuenta suspendida (pago pendiente o incidente). Escríbenos.
422 Datos inválidos: el cuerpo detail dice qué campo. Ejemplos: gratuita con precio 0, RUC con 10 dígitos, moneda no soportada.
429 Límite por minuto. Respeta Retry-After.
503 Tipo de cambio de SUNAT no disponible (solo USD sin tipo_cambio). Reintenta en unos minutos o envía tipo_cambio.

Consultar

Descargar XML, CDR y PDF

GET /v1/emisiones/{id}/documentos

{ "numero": "F001-00000042", "xml": "<?xml …", "cdr": "<?xml …", "pdf_base64": "JVBERi0…", "hash_documento": "…" }

Se sirven desde nuestro respaldo, así que están disponibles aunque el PSE ya no los tenga.

Anular

POST /v1/emisiones/{id}/anular con { "motivo": "Pedido cancelado" }. Las facturas se anulan con comunicación de baja; las boletas, en el resumen diario. En ambos casos la respuesta trae el nuevo estado.

Reenviar el comprobante por correo

POST /v1/emisiones/{id}/reenviar-correo con { "correo": "[email protected]" }. Solo se acepta el correo del receptor original o uno que tú confirmes; 5 por minuto.

Webhooks

Recibe un aviso cuando un comprobante cambia de estado, sin consultar.

Registrar

PUT /v1/webhook con { "url": "https://tusitio.pe/billerpro" } (solo https). Respuesta:

{ "url": "https://tusitio.pe/billerpro", "secreto": "whsec_…", "aviso": "Guarda este secreto…" }

GET /v1/webhook muestra la URL registrada; DELETE /v1/webhook la quita. Cada PUT genera un secreto nuevo.

Qué recibes

POST a tu URL con JSON y cabeceras X-BillerPro-Evento y X-BillerPro-Firma:

{
  "evento": "aceptado",
  "comprobante": { "id": "emi_01J…", "numero": "B001-00000107", "tipo": "03", "serie": "B001",
                   "correlativo": "107", "estado": "aceptado", "total": "59.90", "moneda": "PEN", "mensaje": null },
  "pedido": { "origen": "miweb", "origen_id": "PED-10021" }
}

Eventos: emitido, aceptado, rechazado, anulado, error.

Verificar la firma

X-BillerPro-Firma es HMAC-SHA256(cuerpo_crudo, secreto) en hexadecimal.

import hmac, hashlib
def valido(cuerpo: bytes, firma: str, secreto: str) -> bool:
    return hmac.compare_digest(hmac.new(secreto.encode(), cuerpo, hashlib.sha256).hexdigest(), firma)
$ok = hash_equals(hash_hmac('sha256', file_get_contents('php://input'), $secreto), $_SERVER['HTTP_X_BILLERPRO_FIRMA']);

Responde 2xx en menos de 10 segundos. Si tu servidor no responde, el aviso se registra como fallido pero la facturación nunca se detiene: el webhook es una comodidad, no un requisito.

Buenas prácticas

¿Te quedó una duda? WhatsApp +51 913 527 489 · Contacto · Libro de Reclamaciones← Centro de ayuda