Guía 2 de 2
Importar facturas desde XML
Usa este flujo cuando tus CFDIs ya se timbraron en otro lado — tu ERP, otro PAC o tu contador — y quieres que Savio los registre y los cobre.
Requisitos previos
Nada que configurar
No necesitas certificados CSD para este flujo, porque Savio nunca timbra nada; solo lee documentos que tú ya emitiste.
Paso 1
Importa una factura
Envía el XML del CFDI a
POST
/invoice/from-cfdi.# jq arma el cuerpo JSON para escapar el XML correctamente como stringjq -Rs '{ xml_text: ., external_invoice_id: "erp-inv-1001" }' cfdi-1001.xml \| curl https://api-sandbox.savio.mx/api/v1/invoice/from-cfdi \-H "Authorization: $SAVIO_API_KEY" \-H "Content-Type: application/json" \-d @-
- El XML va en el cuerpo JSON como un string en
xml_text. No es una carga de archivo multipart. - El límite de tamaño del cuerpo para este endpoint es de 10 MB.
- Savio lee todo del XML: emisor, receptor, conceptos, impuestos, totales, moneda y la forma/método de pago. No vuelves a capturar nada de eso.
- La fecha de vencimiento sale de los términos de pago del cliente. Anúlala con
due_date. - Envía
status: 'unready'para importar la factura sin que entre a cobranza todavía.
Cómo se manejan los clientes
Por defecto, los clientes se crean por ti. Si omites
customer_id y external_customer_id, Savio busca el RFC del receptor entre tus clientes existentes y, si no encuentra coincidencia, crea el cliente, la dirección y el contacto a partir del nodo del receptor en el XML.Para asociar la factura a un cliente que ya conoces, fíjalo de forma explícita:
await savio('/invoice/from-cfdi', {xml_text,external_invoice_id: 'erp-inv-1001',external_customer_id: 'crm-4821',})
Cuando fijas un cliente, el RFC de ese cliente debe coincidir con el RFC del receptor en el XML, o la petición se rechaza con
RFC_MISMATCH. Un cliente fijado ya debe tener un contacto — Savio no creará uno en este caso.| Lo que envías | Resultado |
|---|---|
Ni customer_id ni external_customer_id | Se busca por RFC; se crea desde el XML si es nuevo |
external_customer_id (o customer_id) | Se usa tal cual; el RFC del receptor debe coincidir |
Importar varias veces de forma segura
Las importaciones son idempotentes por dos vías independientes, así que los reintentos y las cargas históricas traslapadas son seguros:
external_invoice_id— reenviar uno que ya usaste devuelve la factura existente en lugar de crear una segunda.- UUID del CFDI — Savio también deduplica por el
sat_uuiddel XML. Si ese UUID ya se importó, obtienesINVOICE_ALREADY_EXISTSjunto conexisting_invoice_id.
En una carga histórica
Trata
INVOICE_ALREADY_EXISTS como éxito, no como error.Paso 2
Importa pagos
Los complementos de pago se importan con
POST
/payment/from-cfdi, con la misma forma.const xml_text = await fs.readFile('./complemento-9931.xml', 'utf8')const payment = await savio('/payment/from-cfdi', {xml_text,external_payment_id: 'bank-tx-9931',})// -> { payment_id, cfdi_id, cfdi_xml_url }
Savio lee del XML a qué facturas aplica el complemento y cuánto va a cada una, y luego lo aplica a las facturas correspondientes. Importa las facturas antes que sus complementos para que las referencias resuelvan.
Paso 3
Importa notas de crédito
Las notas de crédito (CFDIs de egreso) se importan con
POST
/credit/from-cfdi.const xml_text = await fs.readFile('./nota-credito-77.xml', 'utf8')const credit = await savio('/credit/from-cfdi', {xml_text,external_credit_id: 'erp-cn-77',})
Todo junto: una carga histórica típica
El orden importa — primero las facturas, luego los documentos que las referencian:
for (const file of invoiceXmls) {await savio('/invoice/from-cfdi', {xml_text: await fs.readFile(file, 'utf8'),external_invoice_id: idFrom(file),})}for (const file of paymentXmls) {await savio('/payment/from-cfdi', {xml_text: await fs.readFile(file, 'utf8'),external_payment_id: idFrom(file),})}
Como ambos endpoints deduplican, puedes volver a ejecutar esto de forma segura tras una falla.
Siguientes pasos
- Concilia los pagos importados con los movimientos bancarios usando GET
/bank-transaction. - Suscríbete a los webhooks para mantener tu sistema sincronizado.