Skip to main content
Cuando un documento termina de causarse en Payana, se dispara el evento document.accounted contra el endpoint que registres. El payload trae el comprobante contable completo y listo para registrar: cada línea con su cuenta, los impuestos discriminados, las retenciones calculadas, la distribución por centro de costo y el cruce contra la orden de compra, la entrada de almacén o el anticipo cuando corresponde. Este es el único momento en que Payana escribe hacia tu lado. Tu sistema es el que decide cómo se traduce a un asiento del ERP.

Configuración

El endpoint receptor y su secret se configuran del lado de Payana; coordinalo con tu equipo de implementación. Necesitás definir:
Este webhook usa un timeout de 120 segundos, más alto que el de los demás eventos, porque tu endpoint tiene que escribir en el ERP antes de responder. Son los dos minutos que medimos como suficientes para registrar un comprobante en la mayoría de los ERPs. Si el tuyo tarda más, avisale a tu equipo de implementación para ajustarlo antes de salir a producción: si Payana corta por timeout una escritura que en realidad estaba en curso, el reintento puede duplicarte el comprobante.

Entrega y respuesta

La entrega es síncrona: Payana espera a que registres el comprobante y le respondas con el resultado. No es un webhook de notificación que se confirma y se procesa después.

Respuesta correcta

Respondé 200 con el identificador y el consecutivo bajo el que quedó registrado el comprobante. Payana lo guarda y se lo muestra al usuario, que así puede encontrar el documento en tu ERP.

Respuesta con error

Si no pudiste registrarlo, respondé 422 con un código y un mensaje. Ese mensaje se le muestra al cliente en Payana, así que escribilo para que una persona de contabilidad entienda qué pasó y qué hacer.
Payana marca la causación como fallida, le muestra el error al cliente y habilita el reintento. Un 5xx, un cuerpo ilegible o exceder el timeout se tratan como fallo genérico y disparan los reintentos automáticos.

Idempotencia

Procesá el webhook de forma idempotente: usá data.reference como llave de deduplicación. Es imprescindible acá, porque un timeout hace que Payana reintente un comprobante que tal vez ya registraste. Si te llega un reference que ya procesaste, no lo registres de nuevo: respondé 200 con el erp_invoice_id y el consecutivo que le habías asignado.

Verificación de la firma

X-Payana-Signature es el HMAC SHA256 del body crudo, con tu secret como clave.
Verificá siempre la firma antes de procesar, y usá una comparación de tiempo constante (timingSafeEqual) en lugar de ===.

Directa o con cruces

Una factura puede causarse de dos formas, y no se registran igual. Casi ningún ERP las recibe por el mismo endpoint —en Dynamics FO, por ejemplo, una va a VendInvoiceJournal y la otra a VendorInvoice—, así que tu integración tiene que distinguirlas antes de escribir. El discriminador es el campo source de las líneas del asiento. Para decidir, recorré las líneas: si alguna tiene source igual a purchase_order o goods_receipt, es una causación con cruces.
Una línea con source: "goods_receipt" también enruta al camino de cruces: la factura igual cruza la orden de compra —la que originó esa entrada—, y la entrada solo precisa por qué cantidades.
En una causación con cruces, cada línea trae purchase_order_line_external_id o goods_receipt_line_external_id, y sus dimensiones —centro de costo, bodega— vienen heredadas del documento cruzado, no elegidas de nuevo. Registrala contra ese documento para que tu ERP descuente lo pendiente; si la registrás suelta, la orden va a quedar abierta por algo que ya se facturó.

Estructura del payload

El documento y su causación viajan juntos: data describe la factura y data.accounting_entry el comprobante contable armado. Las convenciones son las mismas que en la entrada: los importes van en centavos, como entero, las fechas en YYYY-MM-DD, las marcas de tiempo en ISO 8601, y las referencias a entidades del catálogo usan el external_id de tu ERP.

Campos del documento

El beneficiary viene de la entidad fiscal, así que identifier_number es el NIT o RFC. Es la forma de cruzar contra el proveedor de tu ERP — la misma llave con la que lo sincronizaste.

El comprobante: accounting_entry

Encabezado del asiento: journal_entry

Todas las referencias a entidades del catálogo vuelven con el external_id que vos enviaste, no con identificadores internos de Payana. La correspondencia la resuelve Payana en los dos sentidos, así que no necesitás mantener una tabla de mapeo propia.

Líneas del asiento: erp_journal_entry_lines

Cada línea es un movimiento contable. debit_cents y credit_cents son excluyentes: una línea mueve por debe o por haber. Valores de source
source es informativo para tu registro, pero es lo que te permite auditar el origen de cada imputación: una línea con source: "purchase_order" y purchase_order_line_external_id te dice exactamente qué línea de qué OC se está facturando, y con eso podés descontar el pendiente en tu ERP.

Contrapartida: payment_lines

La cuenta por pagar generada. Cuando hay pago en cuotas, viene una línea por cuota.

Cruce de anticipos: advance_crossings

Aparece cuando la factura se descontó contra uno o más anticipos. Después de procesar un cruce, actualizá el saldo del anticipo en tu ERP y volvé a sincronizarlo con outstanding_amount_cents, para que Payana no lo vuelva a ofrecer por un saldo que ya se consumió.

Ejemplo completo

Una causación con cruces: factura de un servicio contra orden de compra, con IVA del 19% y retención en la fuente del 11%. Se reconoce por el source: "purchase_order" de la línea y su purchase_order_line_external_id.
En este ejemplo, el asiento a registrar es: La retención se resta de la cuenta por pagar: el proveedor cobra $1.296.000, no el total de la factura. Las líneas de impuesto vienen discriminadas dentro de taxes y su cuenta contable es la que sincronizaste en default_book_account_external_id de cada impuesto.

Próximos pasos