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: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.
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.
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 aVendInvoiceJournal 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.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
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 elsource: "purchase_order" de la línea y su purchase_order_line_external_id.
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
- Catálogo de entidades — el contrato de entrada.
- Cómo funciona la integración — arquitectura y modelo de identidad.
- Referencia del evento
document.accounted— el esquema en OpenAPI.