Facturación desde tu sistema
Envía boletas y facturas desde tu aplicación usando el mismo formato JSON para ambos documentos.
URL base
Antes de empezar
- Configura en el panel el negocio, su RUC, conexión SUNAT y series.
- Obtén una clave de integración propia para ese negocio.
- Desde el servidor de tu sistema, envía el comprobante con una referencia ?nica de tu venta.
- Guarda el
datos.idrecibido para consultar el resultado y descargar los archivos.
La empresa, credenciales SUNAT, series y diseño PDF se administran en el panel. Estos datos no se configuran mediante esta API. Por ahora solo están disponibles boletas y facturas.
Tu primera llamada, explicada
Una llamada HTTP es un pedido que tu sistema hace a AIONVEX. Para emitir, envia tres cosas: la URL del documento, los headers con tus credenciales y la referencia de venta, y el body con los datos del cliente y los productos.
- Prepara el negocio: ingresa al panel de proveedores, completa el perfil y coordina con AIONVEX la activacion, series y credenciales. La cuenta de prueba no emite.
- Elige el documento: boleta usa
POST /v1/boletas; factura usaPOST /v1/facturas. - Completa los datos: cambia fecha, serie, cliente y productos por los de tu venta.
- Guarda el resultado: conserva la referencia de venta y el
datos.id. Luego consulta el estado; creado no significa aceptado por SUNAT.
Ejemplo con comentarios // en JavaScript
Copia este ejemplo en un archivo emitir.mjs. Se ejecuta en tu servidor con Node.js 18 o superior y las variables AIONVEX_API_KEY y AIONVEX_API_SECRET configuradas. Los valores del cliente son ficticios: reemplazalos antes de emitir.
JSON.stringify produce el JSON sin comentarios. Para Postman o cURL, usa los bloques JSON de las secciones Boleta y Factura.// Ejecuta este ejemplo en el servidor de tu sistema con Node.js 18 o superior.
// Guarda las credenciales en variables de entorno; no las pongas en el navegador.
const apiKey = process.env.AIONVEX_API_KEY;
const apiSecret = process.env.AIONVEX_API_SECRET;
if (!apiKey || !apiSecret) throw new Error("Configura las dos credenciales del negocio.");
// Usa la referencia REAL de la venta en tu sistema y guardala antes del envio.
// Dos ventas diferentes necesitan referencias diferentes.
const referenciaVenta = "VENTA-EJEMPLO-001";
const comprobante = {
serie: "B001", // Serie de boleta habilitada para este negocio.
fecha_emision: "2026-10-08", // Reemplaza por la fecha de la operacion: AAAA-MM-DD.
tipo_moneda: "PEN", // PEN = soles. Tambien se admiten USD y EUR.
forma_pago: "Contado", // Este ejemplo no incluye credito ni cuotas.
cliente: {
tipo_doc: "1", // "1" = DNI. Para factura usa "6" = RUC.
num_doc: "00000001", // Dato ficticio. Usa el documento real como TEXTO.
razon_social: "CLIENTE DE EJEMPLO" // Nombre del cliente o su razon social.
},
items: [{
codigo: "P001", // Codigo de tu producto o servicio.
descripcion: "Producto de ejemplo",
unidad: "NIU", // NIU = unidad de producto.
cantidad: 1, // Numero mayor que cero; no lleva comillas.
precio_unitario: 118.00, // Precio por unidad. Usa la configuracion de precios del negocio.
tip_afe_igv: "10", // "10" = operacion gravada con IGV.
porcentaje_igv: 18 // Porcentaje de IGV de este ejemplo gravado.
}]
};
// JSON.stringify convierte el objeto a JSON valido: los comentarios NO se envian.
// Para una factura: cambia /boletas por /facturas, serie por F001,
// tipo_doc por "6" y num_doc por el RUC real del cliente (11 digitos).
const respuesta = await fetch("https://api.aionvex.com/v1/boletas", {
method: "POST", // POST crea un comprobante.
headers: {
"X-Api-Key": apiKey, // Identifica el negocio que emite.
"X-Api-Secret": apiSecret, // Autentica la clave de ese negocio.
"Content-Type": "application/json",
"Idempotency-Key": referenciaVenta // Evita crear dos veces la misma venta.
},
body: JSON.stringify(comprobante)
});
const resultado = await respuesta.json();
if (!respuesta.ok) {
// HTTP 422: muestra cada campo y sus mensajes para corregir la venta.
if (respuesta.status === 422) {
for (const [campo, mensajes] of Object.entries(resultado.errores ?? {})) {
console.error(campo, mensajes.join("; "));
}
} else {
console.error("HTTP", respuesta.status, resultado.mensaje);
}
// No generes otra referencia ni hagas un reintento automatico.
// Para 409 o 503, conserva la referencia y revisa el resultado con AIONVEX.
throw new Error("La emision no se confirmo.");
}
console.log("ID para guardar en tu venta:", resultado.datos?.id);
console.log("Respuesta:", resultado);
// Guarda datos.id junto con referenciaVenta en tu base de datos.
// HTTP 201 significa creado; consulta GET /boletas/{id} para conocer el estado SUNAT.
Como usar los ejemplos con Postman
- Crea una peticion
POSTy pega la URL completa del documento. - En Headers, agrega los cuatro headers de la tabla siguiente y reemplaza las credenciales y la referencia.
- En Body ? raw ? JSON, pega el bloque JSON de Boleta o Factura, sin comentarios.
- Revisa los datos antes de pulsar Send: con un negocio activo se crea un comprobante.
Que debes cambiar para una factura
| Dato | Boleta del ejemplo | Factura |
|---|---|---|
| URL | /v1/boletas | /v1/facturas |
| Serie habilitada | B001 | F001 |
| cliente.tipo_doc | "1" (DNI) | "6" (RUC) |
| cliente.num_doc | DNI de 8 digitos, como texto | RUC de 11 digitos que empiece por 10 o 20, como texto |
Las series B001 y F001 son ejemplos: utiliza las series habilitadas para tu negocio. El RUC del cliente corresponde al comprador; el negocio emisor se identifica con tus credenciales.
Credenciales y headers
Cada clave pertenece a un negocio. No envíes un identificador de empresa para elegir otro negocio.
| Header | Uso |
|---|---|
X-Api-Key | Clave de integración del negocio. Obligatoria en todas las llamadas. |
X-Api-Secret | Secreto de esa clave. Obligatorio en todas las llamadas. |
Content-Type: application/json | Obligatorio para emitir. |
Idempotency-Key | Obligatorio para emitir: referencia ?nica de tu venta, por ejemplo VENTA-2026-001. Máximo 128 caracteres: letras, números, punto, guion, guion bajo o dos puntos. |
Las credenciales SUNAT permanecen en el panel. Usa ?nicamente las credenciales de integración entregadas para tu negocio.
Panel de proveedores
Ingresa en api.aionvex.com/login para crear negocios de prueba y completar el perfil fiscal. Cada proveedor ve sus propios negocios. La activacion de emision y entrega de credenciales se coordina con AIONVEX.
Datos del comprobante
| Campo | Tipo | Descripción |
|---|---|---|
serie | Texto | Obligatoria. 4 caracteres: B para boleta o F para factura, seguidos de 3 letras mayúsculas o números. Debe estar activa y configurada para el negocio. |
fecha_emision | Texto | Obligatoria. Ejemplo: 2026-10-08. |
correlativo | Entero | Opcional, mayor o igual a 1. Si se omite, se asigna el siguiente de la serie. |
tipo_moneda | Texto | PEN, USD o EUR. |
forma_pago | Texto | Contado o Credito. Si es Credito, incluye cuotas. |
cliente | Objeto | Obligatorio: tipo_doc, num_doc y razon_social. Dirección, email y teléfono son opcionales. |
items | Lista | Obligatoria, entre 1 y 500 líneas. Cada una incluye descripcion, unidad, cantidad y precio_unitario. |
Productos, servicios y precios
Cada elemento de items es una linea de la venta. Para dos productos, agrega dos objetos a la lista. cantidad es lo vendido y precio_unitario es el precio de una unidad, no el total de la linea. Usa numeros con punto decimal: 118.50, sin S/ ni separadores de miles. La interpretacion del precio debe coincidir con la configuracion de precios del negocio.
Cliente e impuestos
Para factura, cliente.tipo_doc debe ser "6" y cliente.num_doc un RUC numérico de 11 dígitos que empiece con 10 o 20. Para DNI usa "1" y 8 dígitos. Envía los documentos como texto.
La afectación y los importes se procesan según la configuración tributaria del negocio. Puedes indicar tip_afe_igv, porcentaje_igv y mto_valor_unitario cuando corresponda. La exoneración debe estar habilitada en el panel. Los ejemplos corresponden a una operación gravada.
Campos adicionales admitidos
Los campos conservan la validación del servicio de facturación. Los totales opcionales se recalculan al crear el comprobante. Los bloques de descuentos, cuotas, anticipos y otros adicionales deben ser consistentes con la operación.
Facturas
anticipos, cliente, cod_local, contrato_colaboracion, correlativo, cuotas, descuentos_globales, detraccion, enviar_automatico, extras, fecha_emision, fecha_vencimiento, forma_pago, guias, items, leyenda, leyendas, mto_igv, mto_imp_venta, mto_oper_exoneradas, mto_oper_exportacion, mto_oper_gratuitas, mto_oper_gravadas, mto_oper_inafectas, observacion, pagos, percepcion, serie, sub_total, sum_otros_descuentos, tipo_moneda, tipo_operacion, total_anticipos, total_descuentos, total_impuestos, valor_venta
Boletas
anticipos, cliente, cod_local, contrato_colaboracion, correlativo, cuotas, descuentos_globales, detraccion, enviar_automatico, extras, fecha_emision, fecha_vencimiento, forma_pago, guias, items, leyenda, leyendas, mto_igv, mto_imp_venta, mto_oper_exoneradas, mto_oper_exportacion, mto_oper_gratuitas, mto_oper_gravadas, mto_oper_inafectas, observacion, pagos, percepcion, serie, sub_total, sum_otros_descuentos, tipo_moneda, tipo_operacion, total_anticipos, total_descuentos, total_impuestos, valor_venta
enviar_automatico puede omitirse o enviarse como true. En esta etapa no se crean comprobantes para enviarlos después.
Una guía relacionada en el campo guias es una referencia del comprobante; no emite una guía nueva.
Emitir boleta
POST /boletas
Ejemplo con datos ficticios. Ajusta serie, fecha, cliente e ?tems a tu operación.
{
"serie": "B001",
"fecha_emision": "2026-10-08",
"tipo_moneda": "PEN",
"forma_pago": "Contado",
"cliente": {
"tipo_doc": "1",
"num_doc": "00000001",
"razon_social": "CLIENTE DE EJEMPLO"
},
"items": [
{
"codigo": "P001",
"descripcion": "Producto de ejemplo",
"unidad": "NIU",
"cantidad": 1,
"precio_unitario": 118,
"tip_afe_igv": "10",
"porcentaje_igv": 18
}
]
}curl --request POST 'https://api.aionvex.com/v1/boletas' \
--header 'X-Api-Key: TU_API_KEY' \
--header 'X-Api-Secret: TU_API_SECRET' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: VENTA-2026-001' \
--data-binary '@boleta.json'Para cURL: guarda el JSON anterior en boleta.json, abre la terminal en esa carpeta y reemplaza TU_API_KEY, TU_API_SECRET y la referencia de venta. El comando mostrado usa continuaciones de linea de Bash; en PowerShell puedes usar curl.exe y escribirlo en una sola linea.
Una respuesta 201 confirma que se creó el comprobante. No confirma por sí sola la aceptación de SUNAT. Consulta el estado con el ID recibido.
Emitir factura
POST /facturas
Ejemplo con datos ficticios. Ajusta serie, fecha, cliente e ?tems a tu operación.
{
"serie": "F001",
"fecha_emision": "2026-10-08",
"tipo_moneda": "PEN",
"forma_pago": "Contado",
"cliente": {
"tipo_doc": "6",
"num_doc": "20000000001",
"razon_social": "CLIENTE DE EJEMPLO"
},
"items": [
{
"codigo": "P001",
"descripcion": "Producto de ejemplo",
"unidad": "NIU",
"cantidad": 1,
"precio_unitario": 118,
"tip_afe_igv": "10",
"porcentaje_igv": 18
}
]
}curl --request POST 'https://api.aionvex.com/v1/facturas' \
--header 'X-Api-Key: TU_API_KEY' \
--header 'X-Api-Secret: TU_API_SECRET' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: VENTA-2026-001' \
--data-binary '@factura.json'Para cURL: guarda el JSON anterior en factura.json, abre la terminal en esa carpeta y reemplaza TU_API_KEY, TU_API_SECRET y la referencia de venta. El comando mostrado usa continuaciones de linea de Bash; en PowerShell puedes usar curl.exe y escribirlo en una sola linea.
Una respuesta 201 confirma que se creó el comprobante. No confirma por sí sola la aceptación de SUNAT. Consulta el estado con el ID recibido.
Consultar estado
Consultar no crea otra venta. Cambia 123 por el datos.id recibido al emitir y conserva el tipo de documento: una boleta se consulta en /boletas y una factura en /facturas. Aqui solo se necesitan los dos headers de credenciales.
GET /boletas/{id} o /facturas/{id}
curl 'https://api.aionvex.com/v1/facturas/123' \
--header 'X-Api-Key: TU_API_KEY' \
--header 'X-Api-Secret: TU_API_SECRET'La respuesta incluye estado, mensaje y datos. Revisa datos.sunat.estado, datos.sunat.codigo y datos.sunat.descripcion.
{
"estado": "exito",
"mensaje": "Estado del comprobante consultado.",
"datos": {
"id": 123,
"numero_completo": "F001-000001",
"sunat": {
"estado": "pendiente",
"codigo": null,
"descripcion": null
}
}
}Respuesta ilustrativa reducida. La respuesta real incluye más campos del comprobante.
Solo puedes consultar documentos emitidos mediante esta integración para el negocio de tu clave. Los archivos pueden tardar en estar disponibles mientras se procesa el envío.
Descargar archivos
| Archivo | Ruta |
|---|---|
GET /facturas/{id}/pdf?format=a4 | |
| XML | GET /facturas/{id}/xml |
| CDR ZIP | GET /facturas/{id}/cdr |
Para boletas, sustituye facturas por boletas. Formatos PDF: a4, a5, ticket-80 y ticket-58. Predeterminado: A4.
curl 'https://api.aionvex.com/v1/facturas/123/pdf?format=a4' \
--header 'X-Api-Key: TU_API_KEY' \
--header 'X-Api-Secret: TU_API_SECRET' \
--output factura.pdfLos enlaces en datos.archivos son relativos, por ejemplo /v1/facturas/123/pdf. Usa el mismo dominio y los headers de autenticación para descargarlos.
Reintentos sin duplicados
- Genera una referencia ?nica de tu venta antes de emitir.
- Guárdala en tu sistema y envíala como
Idempotency-Key. - Si repites la misma referencia con el mismo JSON, se devuelve la respuesta guardada, sin otro envío. El header
Idempotency-Replayed: trueidentifica esa respuesta. - Si cambias el JSON, recibirás
409. No reutilices referencias entre ventas distintas.
Un 422 indica que los datos no superaron la validación. Corrige la información; una referencia ya registrada con otros datos no se sobrescribe.
Validación inmediata
Ejemplo: items.0.cantidad significa la cantidad de la primera linea de tu venta; items.1.cantidad seria la segunda. Muestra estos mensajes junto al campo correspondiente en tu sistema.
Los errores que detectamos antes del envío responden en la misma petición con HTTP 422. El comprobante no se envía al servicio de facturación.
Siempre usamos estado, mensaje y un objeto errores. Cada campo contiene una lista de mensajes, incluso cuando solo existe un error. Se agrupan los errores locales detectados para corregirlos juntos.
{
"estado": "error",
"mensaje": "Error de validación",
"errores": {
"cliente.num_doc": [
"El RUC debe contener exactamente 11 digitos."
],
"items.0.cantidad": [
"La cantidad debe ser un numero mayor que cero."
]
}
}Los nombres corresponden al JSON enviado: cliente.num_doc, serie o items.0.cantidad (el índice comienza en cero). Los errores de headers y query usan Idempotency-Key y format. Si el servicio de facturación no identifica un campo, se usa _general.
Si la validación posterior del servicio de facturación falla, devuelve la misma estructura. Los errores generales de credenciales, permisos o disponibilidad mantienen su código HTTP y estado / mensaje.
Errores y límites
| HTTP | Qué significa |
|---|---|
| 400 | JSON no válido o el cuerpo no es un objeto. |
| 401 | Credenciales inválidas, revocadas o negocio sin acceso activo. |
| 403 | Tipo de comprobante deshabilitado para el negocio. |
| 404 | Ruta no disponible, documento de otro negocio o archivo aún no disponible. |
| 405 / 415 | Método HTTP o Content-Type incorrecto. |
| 409 | Referencia en proceso, en revisión, usada con datos distintos o configuración fiscal inconsistente. |
| 413 / 422 | Cuerpo demasiado grande o datos que no pasan la validación. |
| 429 | Límite por minuto. Respeta el header Retry-After. |
| 503 | Servicio/configuración no disponible o emisión sin respuesta confirmada. Conserva la referencia. |
Límites iniciales: 2 MB (2 000 000 bytes) por envío, 500 ?tems y 60 peticiones por minuto por clave. El límite de peticiones puede ajustarse para el negocio. También se aplican los permisos y límites del plan de facturación.
No hay endpoints públicos para configurar empresas, emitir notas, guías, borrar comprobantes ni reenviar emisiones.