Conciliación de pagos automatizada: deja de cerrar la caja a mano

Cómo automatizar la conciliación con la API Pagnovo — externalId, consulta de transacciones, saldo por categoría, x-trace-id y la rutina que cierra el día sola.

Equipo Pagnovo · 2026-08-06

Conciliar responde una pregunta simple: ¿lo que el sistema dice que recibí coincide con lo que realmente entró? Hecho a mano, en una planilla, el costo aparece de tres formas — horas de trabajo, error humano y el descubrimiento tardío de pedidos pagados que nunca se entregaron.

Esta guía muestra cómo automatizarlo.

El error que vuelve difícil la conciliación después

La decisión más importante ocurre al crear el cobro, no al cierre de mes: enviar el externalId.

POST /transactions/v2/purchase

{
  "amount": 18990,
  "externalId": "pedido-1042",
  "description": "Pedido #1042"
}

El externalId es el identificador del pedido en tu sistema. Sin él dependes de guardar el ID de Pagnovo en algún lugar — y si ese registro se pierde (fallo a mitad de la petición, timeout, un deploy malo), la transacción queda huérfana: dinero recibido sin pedido correspondiente.

Con externalId, la búsqueda funciona en ambos sentidos, y el mismo endpoint acepta tres claves:

GET /transactions/:id

Resuelve por ID de Pagnovo, por tu externalId o por el end2End del PIX. Eso cubre prácticamente cualquier escenario de investigación.

Las tres fuentes que deben coincidir

Una conciliación honesta compara tres cosas:

Fuente Qué responde
Tu base de datos Lo que esperaba recibir
API de Pagnovo Lo que se procesó efectivamente
Saldo de la cuenta Cuánto está realmente disponible

Si solo comparas las dos primeras, no percibes valores retenidos. Por eso importa la tercera.

Saldo: no todo lo que entró está disponible

GET /accounts/balance

La respuesta separa el saldo en categorías — y esa separación es lo que evita el susto a fin de mes:

Un error común es sumar todo y creer que es caja. No lo es: la reserva de contracargo y el bloqueo cautelar existen pero no pueden usarse. Tu conciliación debe tratar cada categoría por separado, y tu flujo de caja debe mirar solo el disponible.

La rutina de conciliación

Un diseño que funciona bien en la práctica, ejecutándose algunas veces al día:

// 1. Pedidos que creé pero aún no confirmé
const pendientes = await db.orders.find({
  status: 'PENDING',
  createdAt: { $gte: ayer },
});

for (const pedido of pendientes) {
  // 2. Le pregunto a la fuente de verdad, usando MI identificador
  const tx = await pagnovo.get(`/transactions/${pedido.externalId}`);

  // 3. Reconcilio el estado
  if (tx.status === 'APPROVED' && pedido.status !== 'PAID') {
    await liberarPedido(pedido, tx);          // el webhook falló — lo recupero aquí
  }
  if (['REJECTED', 'BLOCKED'].includes(tx.status)) {
    await marcarFallo(pedido, tx.status);
  }
  if (tx.status === 'INCONSISTENT') {
    await señalarParaHumano(pedido, tx);      // no decidas solo
  }
}

Tres principios incorporados ahí:

  1. La API es la fuente de verdad, no tu base de datos.
  2. El job repara lo que el webhook perdió. El webhook es el camino rápido; la conciliación es la red.
  3. INCONSISTENT no se resuelve automáticamente. Un estado ambiguo va a revisión humana — con dinero, adivinar sale caro.

Los estados y qué hacer con cada uno

Status Significado Acción
PENDING Esperando pago Mantener en observación, respetar expiración
APPROVED Pagado Liberar pedido (idempotente)
REJECTED Rechazado Cerrar, ofrecer nuevo intento
INCONSISTENT Divergencia Revisión manual
BLOCKED Bloqueado Revisión + contacto con soporte

Paginación: no pierdas registros por el camino

Al listar, la API usa paginación indexada en cero, con límite por defecto de 20 y máximo de 100.

?page=0&limit=100

El error clásico es leer solo la primera página y concluir que "hubo 20 transacciones en el período". Recorre siempre hasta la última página antes de cerrar cualquier número.

x-trace-id: lo que salva a tu soporte

Cada respuesta de la API incluye el header x-trace-id. Guárdalo junto al registro de la transacción.

Cuando surja un caso del tipo "el cliente jura que pagó", abres un ticket con el x-trace-id y el equipo encuentra exactamente esa petición en los logs — en vez de una investigación aproximada usando hora e importe.

Es una línea de código en tu cliente HTTP que ahorra horas después.

Probar el flujo de conciliación

En el sandbox (sk_test_*), los importes generan resultados determinísticos — incluidos los incómodos:

Importe Estado generado
R$ 10,00 Aprobado
R$ 10,01 Rechazado
R$ 10,02 Inconsistente
R$ 10,04 Contracargo
R$ 10,05 Reembolso
R$ 10,06 Bloqueado

Es decir: puedes probar a propósito el camino del INCONSISTENT y del contracargo, que son justamente los que rompen una conciliación mal hecha. El entorno de pruebas permite 30 operaciones por día.

Checklist


Conoce nuestra plataforma de Cobros y la documentación de la API. ¿Necesitas ayuda para diseñar tu conciliación? Habla con el equipo.