Conciliación diaria

Trae una vez al día todos los pagos que Makatea registró y cuádralos contra tu sistema y tu banco.

Los webhooks avisan al momento, pero una entrega puede fallar. Esta pasada diaria es la red de seguridad. Lee el libro de pagos con GET /v1/payments desde el último punto que guardaste.

El libro de pagos

Cada movimiento de dinero es un renglón que no se edita. Hay dos tipos.

  • kind: "payment" es un pago o abono.
  • kind: "reversal" es la anulación de un pago. Lleva el monto en negativo y voids_payment_id apunta al pago anulado.

Cada renglón trae dos fechas.

  • paid_at es cuándo entró el dinero. Puede ser de días atrás.
  • recorded_at es cuándo se anotó en Makatea. Es la que usa la sincronización.

Un pago con fecha de la semana pasada que alguien registró hoy aparece en tu corrida de hoy. Por eso el punto de control es recorded_at y no paid_at.

La corrida

  1. Lee tu último punto de control. La primera vez usa la fecha de arranque.
  2. Pide GET /v1/payments?updated_since=<punto>&limit=500. Con ese filtro los renglones vienen del más viejo al más nuevo.
  3. Procesa la página. Mientras pagination.has_more sea true, pide la siguiente con cursor=<next_cursor> y los mismos filtros.
  4. Al terminar guarda el recorded_at del último renglón como tu nuevo punto.

El filtro incluye el instante exacto del punto, así que el último renglón de ayer vuelve a salir hoy. Por eso cada renglón se aplica una sola vez por su id.

curl -s "$MAKATEA_API/v1/payments?updated_since=2026-09-26T06:00:00Z&limit=500" \
  -H "Authorization: Bearer $MAKATEA_KEY"
{
  "data": [
    {
      "id": "b7e2…",
      "billing_order_id": "91d0…",
      "member_id": "6f1c…",
      "amount": 1850,
      "kind": "payment",
      "method": "transfer",
      "reference": "SPEI-4471",
      "voided": false,
      "paid_at": "2026-09-25T18:00:00Z",
      "recorded_at": "2026-09-26T15:12:40Z",
      "allocation": { "capital": 1400, "interest": 450, "late_fee": 0, "other": 0, "unassigned": 0 }
    }
  ],
  "pagination": { "limit": 500, "has_more": false, "next_cursor": null }
}

Un ejemplo completo en Node.

const API = process.env.MAKATEA_API;
const KEY = process.env.MAKATEA_KEY;

export async function conciliar(desde) {
  let cursor = null;
  let punto = desde;
  for (;;) {
    const q = new URLSearchParams({ updated_since: desde, limit: "500" });
    if (cursor) q.set("cursor", cursor);
    const r = await fetch(`${API}/v1/payments?${q}`, { headers: { Authorization: `Bearer ${KEY}` } });
    if (r.status === 429) {
      await new Promise((ok) => setTimeout(ok, Number(r.headers.get("Retry-After") ?? 5) * 1000));
      continue;
    }
    if (!r.ok) throw new Error(`Makatea ${r.status}: ${await r.text()}`);
    const { data, pagination } = await r.json();
    for (const renglon of data) {
      await aplicarEnMiSistema(renglon); // idempotente por renglon.id
      punto = renglon.recorded_at;
    }
    if (!pagination.has_more) return punto; // guárdalo para la siguiente corrida
    cursor = pagination.next_cursor;
  }
}

Cómo aplicar cada renglón

  • Usa id como llave. Si el renglón ya existe en tu sistema, sáltalo. La corrida se puede repetir sin miedo.
  • Una anulación resta. Aplica el reversal con su monto negativo y marca el pago original como anulado.
  • allocation dice cómo se repartió. Capital, interés, moratorio, otros cargos y lo que queda por aplicar. Es null cuando la cuota no tiene desglose.
  • Cruza contra el banco con reference. Es la referencia que se capturó al registrar el pago, por ejemplo la clave de rastreo del SPEI.

Cuadre del día

Al final de la corrida compara tres números.

NúmeroDe dónde sale
Suma de amount de los renglones con paid_at de ayerGET /v1/payments
Depósitos de ayer en tu estado de cuentaTu banco
Cobrado de ayerGET /v1/stats?from=<ayer>&to=<ayer>, campo metrics.collected

metrics.collected excluye pagos anulados y aplicaciones de saldo a favor. Si los tres números no coinciden, la diferencia está en un pago sin registrar o en un depósito sin referencia.

Los otros objetos

El mismo patrón sirve para cuotas y clientes. GET /v1/billing-orders?updated_since=… y GET /v1/members?updated_since=… devuelven lo que cambió, ordenado por updated_at, con el mismo cursor. Ahí el punto de control es el updated_at del último renglón.

Esta guía en markdown.